728x90

S3에 업로드된 파일의 이름변경, 언제 필요할까요?

특정한 이름 구조나 패턴을 갖춘 파일들이 필요할 때가 분명히 있습니다.

제가 이번에 해당 경험을 하게되어 간단히 정보를 공유해보고자 합니다.

 

 

 

S3에서 제공하는 API를 활용하면 제법 다양한 일들을 할 수 있습니다.

그중에서 이번엔 버킷에서 항목들의 목록을 가져오고, 항목들의 이름을 변경하는 방법에 대해서 알아보고자 합니다.

먼저, S3 API 중에 항목의 이름을 변경하는 API는 존재하지 않습니다.

따라서, 항목을 카피하는 copyObject를 이용해  변경된 이름의 Object를 생성하고 원본을 지우는 방식을 채택해야합니다.

 

항목들을 가져올때는 listObjectsV2를 사용하는데요 간단한 예시를 들어보겠습니다.

const config = {
  accessKeyId: process.env.AWS_KEY,
  secretAccessKey: process.env.AWS_SECRET_KEY,
  region: 'ap-northeast-2',
  signatureVersion: 'v4',
};

AWS.config.update(config);
const s3 = new AWS.S3();

s3.listObjectsV2({ Bucket: `pdf.pitchit`, MaxKeys: 1000 }, async (err, data) => {
  const { Contents } = data;
  const keys = Contents.map(info => info.Key);
  
  // 키를 활용해 다양한 액션을 취할 수 있습니다.
  // 그중에서 객체 복사는 다음과 같이 간단히 할 수 있습니다.
  
  const info = {
    Bucket: 'origin_bucket',
    CopySource: `/target_bucket/${key}`,
    Key: new_name, // 변경할 이름입니다.
  };
  
  s3.copyObject(info, async (err, data) => {
    ...
  });
});

 

 

 

728x90
반응형
728x90

프로젝트를 오래 진행하다보면

commit  / tag / branch등으로 인해 .git/objects폴더가 굉장히 거대해진다.

로컬에서는 문제없지만, 스토리지 사이즈가 작은 Linux서버에서는 제법 치명적이다.

그래서 해당 폴더를 관리하기위한 방법을 찾았다.

 

=============================================

git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.mov' -- --all

rm -Rf .git/refs/original

rm -Rf .git/logs/

git gc --aggressive --prune=now

==========================================================================================

 

 

나는 마지막 한줄만을 사용중이다.

728x90
반응형
728x90

웹개발을 하다보면 브라우저 호환성으로 인해 스트레스를 많이 받습니다.

 

하지만, 종종 특수한 경우에 모든 브라우저를 지원하기위해 노력할 필요가 없을때가 있습니다.

예를들어, 개발 초기부터 "IE는 배제하고 다른 사용자 브라우저에 집중하기로 한다"하고 미리 약속을 할 때가 있죠.

 

그런 조금은 행복한 경우를 위해,

"ancient"브라우저들을 nginx수준에서 막아버릴 방법을 제시해드리도록 하겠습니다.

(의외로 잘 정리된 자료가 없더군요)

이 방법은 Nginx가 늘 사용자 request의 브라우저를 식별하고 있음을 이용한 방법입니다.

 

# /etc/nginx/nginx.conf

...
http {
    ...
    map $http_user_agent $outdated {
        default                                 0;
        "~MSIE [1-9]\."                         1;
        "~MSIE [0-9][0-9]\."                    1;
        "~Trident/.*"                           1;
        "~Edge/[0-9]\."                         1;
        "~Edge/[0-9][0-9]\."                    1;
        "~Edge/.*"                              1;
        "~Mozilla.*Firefox/[1-9]\."             1;
        "~Mozilla.*Firefox/[0-2][0-9]\."        1;
        "~Mozilla.*Firefox/3[0-1]\."            1;
        "~Opera.*Version/[0-9]\."               1;
        "~Opera.*Version/[0-1][0-9]\."          1;
        "~Opera.*Version/2[0-1]\."              1;
         "~AppleWebKit.*Version/[0-6]\..*Safari" 1;
         "~Chrome/[0-9]\."                       1;
        "~Chrome/[0-2][0-9]\."                  1;
        "~Chrome/3[0-3]\."                      1;
    }
    ...
}
...

 

다음은, /etc/nginx/sites-enabled (혹은, /etc/nginx/sites-available/default)를 수정할건데요.

여러분이 설정해둔 server중 적용하고 싶은 파트에 적절히 배치하면됩니다.

예시를 통해 확인하고 응용해보세요.

# /etc/nginx/sites-enabled/default 혹은 /etc/nginx/sites-available/default

...

server {
    root /home/ubuntu/hello_world
    index index.html
    server_name www.helloworld.com
    
    location / {
        if ($outdated = 1) {
            root /home/ubuntu/unsupported;
            rewrite ^ /index.html break;
        }

        try_files $uri $uri/ /index.html;
    }
    ...
}

...

보시는 바와 같이 1번 블럭 코드의 브라우저(및 버전)에 해당될경우

/home/ubuntu/unsupported 경로의 index.html 파일을 response합니다.

unsupported경로에 index.html 파일만 적절히 만들어두면 되겠죠?

 

코드를 통해 알수있듯이, IE브라우저 외에도 오래된 브라우저들은 모두 redirect 시켜버립니다.

확인해보고 필요한 부분의 코드만 (제외하고싶은 브라우저만) 남겨서 사용하시면 될것같습니다.

 

# 참고자료

https://gist.github.com/rosskevin/5cfd78c73a10ca1989696350d76ea3c1

728x90
반응형

'개발, 코딩 > Nginx' 카테고리의 다른 글

웹사이트 속도 개선방법  (0) 2020.09.25
웹소켓, 400에러해결 / node가 아니라 nginx!?  (0) 2020.03.06
728x90

몽고DB에서 어떤 Array 필드에 아무런 값도 존재하지 않는 경우에는 필터링하는 방법에 대해 알아보겠습니다.

 

일반적으로 필드에 값이 존재하는지 판단할때는 $exists를 사용합니다.

...,
{
  $match: {
    name: { $exists: true }
  }
},
...

이와 같이 아주 간단히 처리할수 있습니다.

 

그치만,  필드 타입이  Array인 경우 또 다른 조치를 취해주어야합니다.

빈 값이라도 []가 들어가있다면 true로 인식하기 때문입니다.

 

따라서, 다음과 같이 처리할수 있습니다.

// 첫번째 방법
...,
{
  $match: {
    foods: {
      $exists: true,
      $ne: []
    }
  },
},
...

// 두번째 방법
...,
{
  $match: {
    foods: {
      $exists: true,
      $not: { $size: 0 }
    }
  },
},
...

두 방법다 유효하며, 저는 두번째 방법을 즐겨씁니다.

두 방법의 연산속도의 차이나 이런 부분에 대해서는 아직 잘 모르지만,

일반적으로 단순값을 통한 비교(여기서는 [] )보다는 아래와 같은 방식이 더 정확하다고 생각하기 때문입니다.

728x90
반응형
728x90

프로젝트를 진행하다보면

목적은 다양하지만 결국 엑셀 출력기능을 사용하게된다.

어드민, 거래처 요구사항 등...

(대시보드가 있지만, 회사내적으로 파일로 관리를 해야한다나..)

 

그래서 이래저래 유용한 엑셀 출력기능을 알아보려고한다.

사용법도 간단하며 매우 유용한 기능이니 필요할때 한번씩 열람해서 보도록하자.

 

먼저, 아래와 같이 라이브러리를 설치하자.

npm i xlsx

(참고로, 해당 라이브러리는 브라우저와 nodejs에서 모두 사용가능하다.)

 

자, 이제 데이터를 엑셀로 출력해보자.

import XLSX from 'xlsx';

const data = [
  {
    index: 1,
    name: 'apple',
    price: '1,200'
  },
  {
    index: 2,
    name: 'grape',
    price: '3,500'
  },
  {
    index: 3,
    name: 'strawberry',
    price: '5,500'
  }
];

const exportDataToXlsx = ({ data, filename }) => {
  const worksheet = XLSX.utils.json_to_sheet(data); // excel sheet하단의 worksheet에 해당
  const new_workbook = XLSX.utils.book_new(); // excel 파일에 해당
  
  XLSX.utils.book_append_sheet(new_workbook, worksheet, 'SheetJS'); // excelsheet를 excel파일에 넣음
  XLSX.writeFile(new_workbook, filename + '.xlsx');
}

 

이와 같이 매우 짧고 간단한 코드로 간단히 excel파일을 출력할 수 있다.

응용한다면, data의 타입이나 기간등에 따라 sheet를 나누어 출력하거 파일을 구분할수도있을것 같다.

 

그 또한 매우 간단하지만, 비개발자 입장에서는 매우 고급기술처럼 보일수있다.

잘 알아뒀다가 고급기술인척 해보도록하자.

728x90
반응형
728x90

리액트로 큰 프로젝트를 진행하다보면 페이지 진입 후 로딩속도가 길어지는 현상을 겪게된다.

리액트의 구조적인 문제로, 모든 파일이 번들링되어 index.html포함되기 때문이다.

이에대한 해결책으로 많이 언급되는것들이있다.

 

그 중에서 Code-splitting을 통한 로딩속도 향상 방법을 소개한다.

특히, 가장 간단하면서도 react에서 기본으로 지원해주는 기능을 이용하도록 하겠다.(v16이상)

 

바로, 리액트의 Suspense와 lazy이다.

간단하게는 Route에서 각 페이지 컴포넌트들을 lazy로딩하는 구조를 취한다.

다시 말하면, Switch를 통해 페이지 컴포넌트가 요청될때 해당 컴포넌트를 import하는 구조이다.

 

기본적인 사용법도 매우 간단하며 Docs도 아주 깔끔하게 잘되어있다.

[React Docs] https://ko.reactjs.org/docs/code-splitting.html

 

코드 분할 – React

A JavaScript library for building user interfaces

ko.reactjs.org

위의 문서에보면 하단부에 잘 나타나있다.

 

import React, { Suspense, lazy } from 'react';
...

const Home = lazy(() => import('./pages/home'));
const Profile = lazy(() => import('./pages/profile'));

...


const App = ({}) => {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>...loading</div>}>
        <Route exact path='/' component={ Home } />
        <Route path='/profile' component={ Profile } />
      </Suspense>
    </BrowserRouter>
  )
}

 

대략, 이처럼 사용할 수 있다.

작은 프로젝트에서는 그 효율이 낮을수있으나 프로젝트가 커질수록 크 효율이 높아지는것으로 보인다.

해당 소스를 적용하는게 어렵다거나 시간적으로 오래걸리는것도 아니니,

처음부터 구조를 잘 갖춰나가면 될것같다.

 

(작은 프로젝트에서 Lazy를 적용함으로써 생기는 문제/단점이 있다면 댓글 부탁드립니다... 궁금해서요)

728x90
반응형
728x90

[1탄] https://honeystorage.tistory.com/188?category=748845

[2탄] https://honeystorage.tistory.com/189?category=748845

 

1,2 탄에 이어서 포스팅을 진행해보겠습니다.

앞서 1,2탄에서는 선행 작업과 템플릿에 대해서 알아봤습니다.

 

이번에는 실제 예제코드를 통해 API요청 테스트를 진행해보도록 하겠습니다.

그 전에, 템플릿에 대해서 조금만 추가 설명을 하고자 합니다.

 

 

알림톡을 보낼때 내용이 한줄작성인 경우는 굉장히 드물것입니다.

그런데 줄바꿈을 하다보면, API요청 과정에서 또 오류를 만나게 되고는 합니다.

보통, javascript에서 일반 문자열과 변수를 섞어서 사용할때 "백틱"이라 불리는 그레이브엑센트를 쓰는데요.

만약에,

// 템플릿
오늘 점심메뉴는 #{ somethingfood }이었습니다.
정말 맛있었습니다.

------------------------------
// 잘못된 예제
const message = `
  오늘 점심 메뉴는 ${food}이었습니다.
  정말 맛있었습니다.
`;


// 잘못된 예제
const message = `
오늘 점심 메뉴는 ${food}이었습니다.
정말 맛있었습니다.
`;

// 올바른 예제
const message = `오늘 점심 메뉴는 ${food}이었습니다.
정말 맛있었습니다.`

 

템플릿에 대해서 감이 오시나요?

템플릿과 소스코드는 변수 부분을 제외하고는 정확하게 내용이 같아야지만 올바른 요청으로 인정됩니다.

자꾸만 에러가 난다면 꼭 템플릿과 코드를 옆에 두고 비교해보시기 바랍니다.

 

 

자, 본격적으로 소스코드 예제를 진행해보도록 하겠습니다.

NCP를 처음 사용하신다면 흔히 막히는 부분이 암호화 서명 관련 섹션입니다.

DOCS에 친절히 나와있지만, nodejs관련해서는 다소 불친절하기 때문이죠.

 

암호화 서명을 위한 예제코드를 살펴보겠습니다.

function makeSignature() {
  var space = ' '; // one space
  var newLine = '\n'; // new line
  var method = 'POST'; // method
  var timestamp = Date.now().toString(); // current timestamp (epoch)
  var accessKey = process.env.NAVER_ACCESSKEY; // access key id
  var secretKey = process.env.NAVER_SECRETKEY; // secret key
  const url2 = `/alimtalk/v2/services/${process.env.NAVER_CHANNEL_ID}/messages`;
  let message = [];
  let hmac = crypto.createHmac('sha256', secretKey);

  message.push(method);
  message.push(space);
  message.push(url2);
  message.push(newLine);
  message.push(timestamp);
  message.push(newLine);
  message.push(accessKey);
  return hmac.update(message.join('')).digest('base64').toString();
}

여기서 주로 살펴볼 부분은,

crypto / NAVER_ACCESSKEY / NAVER_SECRETEKY / NAVER_CHANNEL_ID 입니다.

 

1. crypto는 nodejs의 내장모듈은 crypto를 의미합니다.

const crypto = require('crypto'); 해주시면 됩니다.

 

2. NAVER_ACCESSKEY는 1탄에서 알려드린 "계정관리"에서 생성한 "API인증키"를 말합니다.

 

3.  NAVER_SCRETKEY "API인증키" 옆에 보이는 "Secret key"입니다.

 

4. NAVER_CHANNEL_ID는 NCP의 easy notification에 등록한 프로젝트에 보면 Biz message가 있죠? 열쇠모양을 누르면 해당 서비스의 id를 확인할 수 있습니다.

 

 

거의 다 됐습니다.

마지막으로 4탄에서는, 인증요청 작업을 API문서와 비교 작성해보며 마무리해보도록 하겠습니다.

728x90
반응형
728x90

[1탄] https://honeystorage.tistory.com/188?category=748845

[2탄] https://honeystorage.tistory.com/189?category=748845

[3탄] https://honeystorage.tistory.com/190

 

1, 2, 3탄에 이어서 포스팅을 진행해보겠습니다.

앞서 1, 2, 3탄에서는 선행 작업과 템플릿, 암호화서명에 대해서 알아봤습니다.

 

자, 이제 마무리입니다.

API연동을 진행함에 있어 Docs를 안볼수는 없겠죠?

https://apidocs.ncloud.com/ko/ai-application-service/sens/alimtalk_v2/

 

우리가  참고할 파트는 메시지 발송입니다.

단일 메시지 발송과 멀티 메시지 발송의 차이는 수신자를 한명만 두느냐, 여러명 두느냐 정도의 차이밖에 없습니다.

따라서, 우리는 단일 메시지 발송에 포커스하여 진행해보도록 하겠습니다.

 

"Mandatory"라고 표기된 값들이 필수값인데요.

그외의 것들은 API요청때 없어도 되는것들입니다. 우리는 "Mandatory"만 파라미터로해서 요청을 날려보도록 하겠습니다.

 

순서는 다음과 같습니다.

1. 암호화서명 함수를 만든다. (3탄에서 진행했습니다.)

2. template코드에 따라 적절한 parameter를 생성해주는 함수를 만든다.

3. 요청한다.

 

"Mandatory" 항목중에 templateCode가 있습니다.

해당 templateCode를 통해 우리의 요청이 어떤 템플릿에 대응되는것인지를 알려주는것입니다.

 

서비스를 운영하면서 템플릿을 한개만 쓰는경우는 없기때문에, 

if문이나 switch문 등을 통해 원하는 parameter를 세팅해서 리턴해주는 함수를 만들도록 하겠습니다.

 

다음은 예시입니다.

const getRequestParams = ({ type, to, data }) => {
  if (type === 'food') {
    return {
      templateCode: type,
      plusFriendId: process.env.MyChannelId,
      messages: [
        {
          to,
          content: `내가 만든 ${ data.food }는 맛있습니다.`,
          buttons: [
            {
              type: 'WL',
              name: '레시피 확인',
              linkMobile: 'https://food.tech.kr',
              linkPC: 'https://food.tech.kr',
            }
          ]
        }
      ]
    }
  }
  
  if (type === 'car') {
    return {
      ...
    }
  }
  
  ...
}

간단히 이런식으로 작성해 나갈수 있겠죠?

switch문으로 하면 더 깔끔할수 있겠네요.

 

이제 정말 마지막입니다.

4탄에 걸쳐서 포스팅을 하고있지만, 막상 해보면 1시간도 안걸려 끝날것입니다.

(시행착오를 겪은 제입장에서는 반나절은 썼지만요..)

 

자, 요청을 해봅시다!

const sendKakaoMessage = async ({ templateCode, to, data }) => {
  const params = getRequestParams({ type: templateCode, to, data });
  
  const { data: result } = await axios.post(`https://sens.apigw.ntruss.com/alimtalk/v2/services/${process.env.NAVER_CHANNEL}/messages`, params, {
    headers: {
      'Content-Type': 'application/json; charset=utf-8',
      'x-ncp-apigw-timestamp': Date.now().toString(),
      'x-ncp-iam-access-key': process.env.NAVER_ACCESSKEY,
      'x-ncp-apigw-signature-v2': makeSignature(),
    },
  });
  
  console.log('과연? ', result);
}

 

어떤가요? 잘 되셨나요?

여기까지 따라오느냐 고생많으셨습니다.

유료서비스 안녕, 이제는 무료로 카톡의 좋은 서비스 이용하시기 바랍니다.

--- 수정 ---

미흡한 포스팅으로 혼란을 야기해드렸다면 죄송합니다. ㅜㅜ

제가 언급한 무료라는 것은, 중간에 API서비스 업체를 끼지 않고도 직접 알림톡을 구현함으로써

API업체를 끼는 비용을 무료로 가져갈수 있다는 얘기였습니다.

-----------

 

ps. 오랜만에 정성스러운 포스팅이네요. 반응보고 앞으로 방향성을 갖도록 하겠습니다.

728x90
반응형
728x90

[1탄] https://honeystorage.tistory.com/188?category=748845

1탄에 이어서 포스팅을 진행해보겠습니다.

앞서 1탄에서는 선행 작업들을 진행했는데요.

 

이번에는 템플릿 검수를 위한 노하우를 공유하고자합니다.

템플릿을 제대로 등록하지 않으면

내가 코드 문제로 API 요청에서 에러가 나는건지, 템플릿이 문제가 있어서 에러가 나는건지 헷갈리게되는

늪에 빠지게됩니다.

 

어렵지 않으나, 어디에도 자료가 없어 정리하니 잘 참고하시기 바랍니다.

NCP의 예제를 보면 변수 부분에는 #{}이렇게 처리를 하라고 나옵니다.

 

근데, 실제 데이터에서 "#{ 홍길동 }" 이라고 입력을해야하나 아니면 "홍길동" 이라고만 입력하면 되는건지

헷갈립니다.

또한, 처음 템플릿을 생성하게되면 흔히 겪는 오류가 띄어쓰기 한칸까지도 모두 똑같이 맞춰야하는데 그것을 지키지 않아서 겪게되는 오류입니다.

 

예를들어 설명해보겠습니다.

저에게는 변수 food가 있습니다.

먼저, 템플릿을 다음과 같이 등록합니다.

// 템플릿 예제

// 예제 1번
저의 점심 메뉴는 #{food}이었습니다.

// 예제 2번
저의 점심 메뉴는 #{somethingfood}이었습니다.

// 예제 3번
저의 점심 메뉴는 food이었습니다.

// 예제 4번
저의 점심 메뉴는 somethingfood이었습니다.

여기서, 적절한 템플릿은 1번과 2번입니다.

 

 

자, 그런데 해당 템플릿에 맞게 코드를 작성할때는 어떻게 해야될까요?

1. 저의 점심 메뉴는 ${ food }이었습니다.
2. 저의 점심 메뉴는 #{${food}}이었습니다.
3. 저의 점심 메뉴는 ???

 

다음 포스팅에서 코드 작성을 이어가보도록 하겠습니다.

728x90
반응형
728x90

무료로 카톡알림 보내기 어렵지 않습니다.

여기서 무료라 하면, 메세지 발송해주는 업체를 쓸필요가 없음을 의미합니다~

 

조건이 있습니다.

1. 개발자이거나 회사에 개발자가 있다.

2. 카카오톡 채널(플러스친구, 구 옐로아이디) 계정이 있어야 한다.(이것은 유료입니다)

 

카카오톡의 API를 직접 이용해도 되지만,

이번에는 네이버 NCP의 easy notification - biz message를 이용해보도록 하겠습니다.

 

굳이, 카톡 API를 안쓰고 네이버를 이용한 이유는 단 한가지입니다.

네이버가 더 쉽게 API를 풀어서 기술해뒀기 때문입니다.

그만큼 들어가는 시간 비용이 줄어들겠죠?

 

 

선행해야할 작업은 카카오톡에서 카카오채널을 개설해야합니다.

두번째는 네이버 NCP(Naver Cloud Platform)에 가입해야합니다. (네이버 연동 가능)

이 작업이 완료되었다는 가정하에 개발관련해서 포스팅을 시작하도록 하겠습니다.

 

카톡알림을 해주는 서비스인 "biz-message"를 이용하기위해서는

https://console.ncloud.com/sens/kakao-talk-channel <<< 이 링크로 이동합니다.

 

여기서 API사용을 위한 몇가지 작업을 먼저 진행하겠습니다.

1. 좌측 네비게이션에서 easy notification - project에서 신규 프로젝트를 생성해줍니다.

 > 이 후, 채널을 생성하고 나면 여기서 해당 서비스의 id및 secret key등을 확인할 수 있습니다.

 

2. 채널등록 - 카카오톡 채널이 없다면 바로 옆에 "채널생성" 버튼도 있으니 여기서 진행하시기 바랍니다.

 

3. API사용을 위해 키를 생성해야합니다.

 > NCP 우측상단의 내 프로필 아이콘을 클릭하면 "계정설정"이 있습니다.

 > 계정설정에서 로그인을 진행한 뒤, 계정관리 - 인증키 관리로 이동합니다.

 > 여기서 신규 API키를 생성합니다.

 

 

이것으로 API사용을 위한 코드를 치기전 진행할 작업을 모두 마쳤습니다.

다음은 템플릿을 등록하게 되는데요.

템플릿은 어떤 내용을 보내는지, 어떤 버튼을 붙일것인지 등을 미리 정한 것을 의미합니다.

템플릿에 대칭되는 코드를 통해 템플릿을 쉽게 관리하고 사용할 수 있습니다.

 

 

다음 포스팅에서, 템플릿 등록 노하우 및 검수등에 소요되는 기간 등을 알려드리도록 하겠습니다.

https://honeystorage.tistory.com/189

 

728x90
반응형