안경잡이개발자

728x90
반응형

  흔히 우리가 앱을 개발할 때는 페이스북(Facebook) 회원가입/로그인 기능을 구현하곤 합니다. 소셜 로그인 기능에서 가장 중요한 것은 서버에서 이를 검증하는 것입니다. 이미 페이스북 계정으로 가입이 된 사용자의 로그인을 처리하는 소스코드는 PHP로 다음과 같이 작성할 수 있습니다. 이 때 해당 서버에서는 이메일(Email)을 기준으로 회원을 구분한다고 가정합니다. 다시 말해 Facebook API로부터 Access Token으로 이메일 정보를 받아 와서, 그것으로 서버에서 로그인 처리를 시켜주는 것입니다.

 

  이미 사용자가 앱에서 로그인을 했다고 가정하고, Access Token을 서버로 보냈을 때 이를 처리하는 예제입니다. 다음의 소스코드를 참고하시면 될 것 같습니다.

// 사용자로부터 Facebook 로그인 이후 Access Token을 받습니다.
$access_token = $this->input_check('access_token');
$client = new \GuzzleHttp\Client();
if($access_token == "") {
    $this->output->set_status_header(400); // 입력 값 형식이 바르지 않습니다.
    exit;
}
try {
    // Facebook Graph API를 이용하여 Email 정보를 받아올 수 있습니다.
    $response = $client->request('GET', 'https://graph.facebook.com/me?fields=name,email&access_token='.$access_token);
    $body = $response->getBody();
    if($response->getStatusCode() == 400) {
        $this->output->set_status_header(500);
        exit;
    }
    $json_response = json_decode($body, true);
    // Facebook Graph API로부터 Email 정보를 받아와 출력합니다.
    echo $json_response['email'];
    /*
    // 아래는 로그인 인증 수행을 위한 처리 로직 예제입니다.
    $email_data['member_email'] = $json_response['email'];
    $result = $this->model_login->check_signup_type($email_data);
    if($result) {
        switch($result->signup_type) {
            case '0' :
                $this->output->set_status_header(503); // 일반 회원으로 가입한 계정입니다.
                exit; break;
            default:
                $info = $this->model_login->get_info_by_email($email_data);
                if(empty($info)) {
                    $this->output->set_status_header(501); // 존재하지 않는 계정입니다.
                    exit;
                }
                $token = array(
                    "exp" => time() + (60 * 60 * 24 * 7), // Refresh Token의 유효 기간은 7일입니다.
                    "member_idx" => $info->member_idx,
                    "token_type" => "refresh_token"
                );
                $jwt = JWT::encode($token, $this->jwt_key);
                $response = array("refresh_token" => $jwt);
                echo json_encode($response);
        }
    } else {
        $this->output->set_status_header(501); // 존재하지 않는 계정
        exit;
    }
    */
} catch (Exception $e) {
    // 예외 처리를 진행합니다.
}

 

728x90
반응형

728x90
반응형

  구글 인 앱 결제(IAP)는 특정한 애플리케이션의 사용자가 인 앱 결제를 이용할 수 있도록 해주는 기능입니다. 일반적으로 인 앱 결제 기능을 앱에 넣는 경우, 앱에서 사용자가 특정한 물품을 구매한 뒤에 서버의 결제 완료 API를 호출하게 됩니다. 그러면 서버의 결제 완료 API는 앱 사용자에게 아이템을 지급하는 등의 처리를 하게 됩니다.

 

  하지만 많은 서버 개발자들은, 구글 인 앱 결제(In App Purchasing)검증 로직을 넣지 않습니다. 이는 매우 위험하다고 할 수 있습니다. 인 앱 결제 기능을 이용할 때는 클라이언트가 서버 결제 완료 API를 호출했을 때, 해당 사용자가 진짜로 결제를 했는지 확인해야 합니다. 예를 들어, 특정한 악성 클라이언트는 실제로 결제를 하지 않았음에도 서버 API 주소를 알아내서, 해당 API를 무한정 호출해 부당한 이익을 취할 수 있습니다.

 

  따라서 이번 시간에는 구글 인 앱 결제(IAP) 인증을 위한 구글 개발자 API를 사용하는 방법을 소개합니다.

 

  ▶ Google Cloud Platform API Library: https://console.cloud.google.com/apis/library

 

  구글 라이브러리는 위 링크에서 모두 확인할 수 있습니다.

 

 

  이후에 다음과 같이 [Google Play Android Developer API]를 검색하여 확인할 수 있도록 합니다.

 

 

  이후에 [Google Play Android Developer API]를 사용하겠다고 설정하시면 됩니다.

 

 

  이 때 [프로젝트]가 없는 경우 [프로젝트 만들기]를 진행하고, [새 프로젝트 생성] 이후에 [사용 설정]을 하시면 됩니다. 반면에 이미 구글 플레이 콘솔에 올라가 있는 앱과 연동되어 있는 프로젝트가 있는 경우 해당 프로젝트에서 그대로 확인하시면 됩니다. 우리가 해야 할 일은 바로 [사용자 인증 정보]를 만드는 것입니다. 다시 말해 특정한 안드로이드 앱의 사용자 결제 내역을 확인할 수 있는 권한을 가지고 있는, '사용자 인증 정보'를 생성해야 하는 것입니다.

 

 

  따라서 [사용자 인증 정보]로 이동한 뒤에 [사용자 인증 정보 만들기]를 눌러 [서비스 계정 키] 탭에 들어갑니다.

 

 

  이후에 [새 서비스 계정]을 선택한 뒤에 [서비스 계정 이름]을 설정합니다. 그리고 역할은 [소유자]로 선택할 수 있습니다. 이제 [생성] 버튼을 눌러서 JSON 파일을 생성하시면 됩니다. 생성 이후에는 JSON 파일이 생성되므로, 이 파일을 저장해주세요. 향후 서버에서 이 JSON 파일을 이용하여 구글 인 앱 결제 인증을 수행할 수 있습니다.

 

  이제 구글 플레이 개발자 콘솔에 업로드가 되어 있는 앱을 확인합니다.

 

▶ 구글 플레이 개발자 콘솔: https://play.google.com/apps/publish

 

  다음과 같이 Play Console에서 [설정] 탭에 들어가 [API 액세스]를 확인합니다. 그리고 만들어진 서비스 계정에 [액세스 권한 부여]를 진행하고, [앱 정보 보기] 및 [재무 데이터 보기]만 체크하여 [사용자 추가]를 진행하시면 됩니다.

 

 

  결과적으로 다음과 같이 [재무 데이터 보기] 권한이 있도록 해주시면 되는 것입니다.

 

 

  이제 재무 데이터를 볼 수 있는 권한이 추가되었으므로, 실제로 서버에서 API를 호출하여 인 앱 결제 내역을 확인할 수 있게 되었습니다. 서버 프로그램에서 이를 구현하기 위해서는, API 문서를 읽어 보시면 됩니다.

  

▶ 구글 결제 관련 개발자 API 명세서: https://developers.google.com/android-publisher/api-ref/purchases/products

 

  명세서 내용을 확인하면, 어떠한 방식으로 인 앱 결제 정보를 가져올 수 있는지 나타납니다. 핵심이 되는 내용은 다음과 같습니다.

 

 

  구글 결제 관련 개발자 API 사용 방법은 다음과 같습니다. 저는 PHP를 기준으로 설명했습니다.

 

cd /home/ubuntu
sudo vi composer.json
# Google API 설치 내용 작성하기
{
  "require": {
    "google/apiclient": "^2.0"
  }
}
# Composer로 설치하기
sudo composer install
ls
# 해당 폴더에 설치되어 있는 vendor 이용하기
# 연동을 위해 Google의 API JSON 키 파일을 /home/ubuntu의 위치에 두기
# 이후에 다음과 같이 사용하기 (클라이언트로부터 product_id와 purchase_token을 받도록)
$client = new Google_Client();
$client->setAuthConfig('/home/ubuntu/IAP.json');
$client->addScope('https://www.googleapis.com/auth/androidpublisher');
$service = new Google_Service_AndroidPublisher($client);
$package_name = "패키지 이름";
$purchase = $service->purchases_products->get($package_name, $product_id, $purchase_token);
# 이후에 다음의 Getter 함수로 각 상태 값 확인 가능
$purchase->getPurchaseState()
$purchase->getPurchaseType()

 

  (+추가) PHP 코드에 대한 자세한 내용은 다음의 포스팅에 기록해 놓았습니다.

 

https://ndb796.tistory.com/296

 

PHP 서버에서 안드로이드 인 앱 결제(IAP) 여부를 검증하는 API 개발하는 방법

일반적으로 안드로이드 어플리케이션을 개발할 때에는 인 앱 결제(IAP) 기능을 구현해야 할 때가 많습니다. 다만 이러한 인 앱 결제 기능을 구현할 때 알아 두어야 할 점이 있습니다. 그것은 바로 클라이언트가 제..

ndb796.tistory.com

 

728x90
반응형

728x90
반응형

  흔히 이사를 갈 때 주소 변경을 하는 것은 귀찮은 작업이 될 수 있습니다. 이때 주소 변경을 일괄적으로 도와주는 서비스가 있습니다. 대표적인 서비스로는 KT Moving이 있습니다. 이름은 KT Moving인데 실질적으로는 상당히 많은 카드사 및 통신사와 제휴가 되어 있기 때문에, 이 서비스만 이용해도 상당히 많은 불필요한 과정을 줄일 수 있습니다.

 

※ KT Moving ※

 

  ▶ KT Moving: http://www.ktmoving.com/

 

  KT Moving 서비스를 이용해 봅시다. 참고로 moving이사라는 뜻을 가지고 있습니다. 가장 먼저 변경된 집 주소를 입력한 뒤에 [주소 변경 신청] 버튼을 누릅니다.

 

 

  이후에 약관에 동의하고, 본인 인증을 진행합니다. 필자는 [휴대폰 인증]을 이용했습니다.

 

 

  KT Moving 서비스 제휴사를 선택합니다. 필자는 번거롭게 느껴져서 그냥 전체 선택했습니다.

 

 

  필자는 [자택] 주소가 변경된 것이므로, 전부 자택으로 선택했습니다.

 

 

  이제 신청 정보를 입력합니다. 본인의 휴대폰 번호, 이메일 주소 등을 확인합니다.

 

 

  결과적으로 다음과 같이 신청이 완료되었습니다.

 

 

※ 농협 주소일괄변경서비스 (2021년 기준으로 운영되고 있지 않음)

 

  농협은 [주소일괄변경서비스]를 제공하고 있습니다. 이것을 이용하면 자신이 이용하고 있는 은행/카드 서비스의 주소를 일괄적으로 변경할 수 있습니다. 사실 농협뿐만 아니라 대부분의 은행 서비스가 이를 지원하고 있습니다. 자신이 이용하는 은행의 인터넷 뱅킹 서비스에 접속하셔서, 주소지 일괄 변경 요청을 진행해주세요.

 

  이는 특정한 금융 회사에게 권한을 위임하여, 자신이 이용 중인 모든 카드에 대하여 주소 변경을 일괄적으로 할 수 있도록 해주는 것입니다. 매우 편리한 기능이라고 할 수 있습니다.

 

  ▶ 농협: https://banking.nonghyup.com

 

  농협 사이트로 들어가서 [로그인]을 진행하시면 됩니다.

 

 

  로그인 이후에는[My뱅크] 탭에서 [고객정보관리] 탭으로 이동한 뒤에 [금융거래 주소일괄변경]을 진행하시면 됩니다.

 

 

  이후에 화면에 나오는 대로 지시에 따르시고, 금융회사를 선택하여 일괄적으로 변경 요청을 진행하시면 됩니다.

 

 

※ 구글 애드센스 ※

 

  구글 애드센스와 같이 외국 기업과 관련한 서비스는, 주소를 일일이 변경해주실 필요가 있습니다. 구글 애드센스의 주소를 변경하는 경우 애드센스에 로그인을 하신 뒤에 [지급] 탭으로 이동하시면 됩니다. 이후에 [설정 관리]를 진행합니다.

 

 

  이후에 다음과 같이 [이름 및 주소] 부분을 변경해주시면 됩니다.

 

 

728x90
반응형

728x90
반응형

  일반적으로 특별한 사유가 없이 군대에 지원을 하지 않은 경우, 군대에 오라는 안내 편지가 오게 됩니다. 다시 말해 자동으로 입영 일자가 결정되어, 언제까지 입대를 하라고 통보가 오는 것입니다. 일반적으로 입영 일자 6개월 전에 입영 안내 편지를 받게 됩니다. 이 때 대학원 진학이나 창업 등의 다양한 사유로 인하여 입영 일자를 연기하는 것이 가능합니다.

 

  흔히 대학을 졸업할 때까지 군대를 가지 않은 사람이, 6개월 정도 대학원이나 창업을 준비하고 있는 상태일 수 있습니다. 이 때는 다음과 같이 생긴 [현역병 입영일자 등 안내] 편지를 받을 수 있습니다. 저는 향후 이번년도 9월부터 공대에서 대학원 석사 과정을 진행하게 될 예정인데, 입대하라는 안내를 받은 것이므로 당황스러웠습니다.

 

 

  당연히 병무청 쪽에서는 제가 최근에 어떤 활동을 하고 있는지, 정확히 파악하기 어렵습니다. 그러므로 제가 적절한 서류를 구비하여 합리적인 사유로 연기 신청을 직접 해야 하는 것입니다. 또한 입영 일자 연기는 입영일 5일 전까지 신청이 가능합니다. 다시 말해 안내 편지가 받은 뒤에 입영 연기 사유가 있다면, 서류를 구비하기 위한 시간을 주는 것입니다.

 

  이에 대한 자세한 내용은 [병무청]의 [복무제도] 탭에서 확인할 수 있습니다.

 

  병무청 입영일자 연기 안내: https://www.mma.go.kr/contents.do?mc=mma0000729

 

입영일자 연기 - 현역병,상근예비역 - 복무제도 - 병역이행안내 - 병무청

병역이행안내INFORMATION --> --> 입영일자 연기 입영일자 연기 개요 현역병 입영 대상자로서 질병 등 부득이한 사유로 지정된 입영일자에 입영할 수 없는 사람이 병역이행일 연기신청서를 제출하면 이를 심사하여 입영일자를 연기하는 제도 신청대상 현역병입영 대상자로서 아래 연기사유에 해당되는 사람. 단, 입영일자/입영부대를 본인이 선택한 사람은 입영일자 연기(질병, 직계 존·비속 간호, 천재·지변, 행방불명, 각군지원 사유자는 제외) 처리 제한 신청시

www.mma.go.kr

 

  위 링크에 들어가 보시면 대학원, 창업 말고도 굉장히 다양한 사유로 입영 연기하는 것이 가능합니다. 입영 연기를 하실 때에는 사이트에 직접 방문하셔서, 자신에게 맞는 입영 연기 사유를 찾으신 뒤에 해당 항목으로 입영 연기 신청을 하시면 됩니다.

 

  예를 들어 질병, 검정고시 준비, 취업자, 출산, 창업, 공무원 시험 준비 등의 다양한 사유로 연기를 할 수 있습니다. 또한 일반적인 경우 연기 신청을 하면 1~2년 정도의 연기를 해줍니다. 당연히 어떤 사유로도 무한히 연기를 할 수는 없고, 28세를 넘어가는 경우 입영 연기를 하기 어렵습니다.

 

  저는 확인을 해보니까 [대학원진학 예정자] 항목으로 입영 연기를 할 수 있다고 안내가 됩니다. 저는 실제로 대학원 합격 및 컨택까지 완료가 된 상태이므로, 무난히 입영 연기를 할 수 있습니다.

 

 

  또한 입영 연기를 하실 때 흔히 실수하시는 부분으로는, 잘못된 경로로 들어가는 것입니다. [현역병 입영일자 등 안내] 우편을 받은 경우 올바른 경로에 들어가서 신청을 하셔야 합니다.

 

잘못된 경로: [병무청 홈페이지 → 병무민원포털 → 군지원 → 입영일자 연기 신청]

올바른 경로: [병무청 홈페이지 → 병무민원포털 → 현역, 상근입영 → 입영일자 연기원 신청]

 

  바로 다음과 같이 [병무민원포털]로 이동하여 [현역, 상근입영] 탭으로 이동하세요.

 

 

  이후에 [입영일자 연기원 신청]을 통해 연기 신청을 할 수 있습니다.

 

 

  이후에 다음과 같이 안내사항에 동의를 하고 신청서를 제출하시면 됩니다. 더불어, 보완할 서류 항목이 있다면 유선이나 문자를 통해 다시 안내를 해주신다고 하네요.

 

 

  대학원 진학이 사유인 경우 졸업 증명서를 제출하면 된다고 하네요. 결과적으로 저는 다음과 같이 졸업 증명서를 제출했습니다.

 

 

  신청이 완료되면 다음과 같이 신청 결과 안내 메시지가 출력됩니다.

 

 

 

728x90
반응형

728x90
반응형

  이번 시간에는 AWS Lambda와 MongoDB를 활용하여 하나의 게시물(Board) API를 완전히 개발해보는 시간을 가져보도록 하겠습니다. 따라서 가장 먼저, 다음과 같이 /board 경로에 GET, POST, PUT, DELETE 메소드의 API를 Gateway로 열어줍니다. 그리고 /board/{proxy+} 경로의 모든 메소드를 사용할 수 있도록 합니다.

 

  /board/{proxy+}로 설정하면, 모든 Path Parameter를 받을 수 있게 됩니다.

 

 

  이후에 소스코드를 작성합니다. 우리가 작성할 게시물 Rest API의 기본적인 명세는 다음과 같습니다.

 

[ 게시물 ]

 

- 게시물 번호

- 게시물 작성자

- 게시물 비밀번호

- 게시물 내용

- 게시물 작성일자

 

  소스코드는 다음과 같습니다.

"use strict";
const mongoose = require('mongoose');
const MONGODB_URI = process.env.MONGODB_URI;

/* Board 오브젝트를 정의합니다. */
const boardSchema = mongoose.Schema({
    id: {
        type: Number,
        required: true
    },
    name: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    content: {
        type: String,
        required: true
    },
    date: {
        type: Date,
        required: true
    }
});

/* 하나의 연결 객체를 반복적으로 사용합니다. */
let connection = null;

const connect = () => {
  if (connection && mongoose.connection.readyState === 1) return Promise.resolve(connection);
  return mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true }).then(
    conn => {
      connection = conn;
      return connection;
    }
  );
};

module.exports.handler = (event, context, callback) => {
  let operation = event.httpMethod;
  let Board = mongoose.model('board', boardSchema);
  let proxy, password;
  switch (operation) {
    case 'GET':
      /* 
        경로: /board
        설명: 전체 게시글 정보를 불러옵니다.
      */
      if(event.pathParameters === null) {
        let query = {};
        if(event.queryStringParameters.name) {
          query.name = {$regex:event.queryStringParameters.name, $options: 'i'};
        }
        if(event.queryStringParameters.content) {
          query.content = {$regex:event.queryStringParameters.content, $option: 'i'};
        }
        // name과 content를 이용하여 검색한 결과를 내림차순으로 반환합니다.
        connect().then(() =>
        Board.find(query)
        .select("-password")
        .sort({id: -1})
        .exec(function(error, boards) {
            if(error) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify(error) });
            }
            else {
              context.done(null, { 'statusCode': 200, 'body': JSON.stringify(boards) });
            }
        }));
      }
      /* 
        경로: /board/:id
        설명: 특정 게시글 정보를 불러옵니다.
      */
      else {
        proxy = event.pathParameters.proxy;
        connect().then(() =>
        Board.findOne({id:proxy})
        .select("-password")
        .exec(function(err, board) {
            if(err) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
            }
            else if(!board) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Board not found.") });
            }
            else {
              context.done(null, { 'statusCode': 200, 'body': JSON.stringify(board) });
            }
        }));
      }
      break;
    case 'POST':
      /* 
        경로: /board
        파라미터: {"name":"작성자","content":"내용","password":"비밀번호"}
        설명: 특정 게시글을 작성합니다.
      */
      let lastId = 0;
      // 가장 최근에 작성된 게시물 번호를 가져옵니다.
      connect().then(() =>
      Board.findOne({})
        .sort({id: -1})
        .exec(function(err, board) {
          if(err) {
            context.done(null, { 'statusCode': 500, 'body': err });
          } else {
            lastId = board ? board.id : 0;
            const { name, content, password } = JSON.parse(event.body);
            const newBoard = new Board({ name, content, password });
            newBoard.date = new Date();
            newBoard.id = lastId + 1;
            // 새로운 글을 등록합니다.
            newBoard.save(function(err, board) {
              if(err) {
                context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
              } else {
                context.done(null, { 'statusCode': 200, 'body': JSON.stringify(lastId + 1) });
              }
            });
          }
        })
      );
      break;
    case 'PUT':
      /* 
        경로: /board/:id
        헤더: password:"현재 비밀번호"
        파라미터: {"name":"작성자","content":"내용","password":"비밀번호"}
        설명: 특정 게시글을 수정합니다.
      */
      proxy = event.pathParameters.proxy;
      password = event.headers.password;
      // 사용자가 입력한 번호의 게시물을 찾습니다.
      connect().then(() =>
      Board.findOne({id:proxy})
      .exec(function(err, board) {
          if(err) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
          }
          else if(!board) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Board not found.") });
          }
          else {
              if(board.password != password) {
                context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Password is incorrect.") });
              } else {
                  const { name, content, password } = JSON.parse(event.body);
                  // 사용자가 입력한 name, content, password에 맞게 정보를 변경합니다.
                  Board.findOneAndUpdate({id:proxy}, { name, content, password })
                  .exec(function(err, board) {
                      if(err) {
                        context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
                      }
                      else {
                        context.done(null, { 'statusCode': 200, 'body': JSON.stringify('success') });
                      }
                  });
              }
          }
      }));
      break;
    case 'DELETE':
      /* 
        경로: /board/:id
        헤더: password:"현재 비밀번호"
        설명: 특정 게시글을 삭제합니다.
      */
      proxy = event.pathParameters.proxy;
      password = event.headers.password;
      connect().then(() =>
      Board.findOne({id:proxy})
      .exec(function(err, board) {
          if(err) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
          }
          else if(!board) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Board not found.") });
          }
          else {
              if(board.password != password) {
                context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Password is incorrect.") });
              } else {
                // 사용자가 입력한 번호에 해당하는 게시물을 삭제합니다.
                Board.findOneAndRemove({id:proxy})
                .exec(function(err, board) {
                    if(err) {
                      context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
                    }
                    else {
                        context.done(null, { 'statusCode': 200, 'body': JSON.stringify('success') });
                    }
                });
              }
          }
      }));
      break;
    default:
      callback(new Error(`Unrecognized operation "${operation}"`));
  }
};

 

  테스트 결과는 다음과 같습니다.

 

1. 게시글 리스트 보기


경로: /board 
설명: 전체 게시글 정보를 불러옵니다. 

 

 

 

2. 게시물 정보 확인하기


경로: /board/:id 

설명: 특정 게시글 정보를 불러옵니다. 

 

 

3. 게시물 등록하기


경로: /board 
파라미터: {"name":"작성자","content":"내용","password":"비밀번호"} 
설명: 특정 게시글을 작성합니다. 

 

 

4. 게시물 수정하기

 

경로: /board/:id
헤더: password:"현재 비밀번호" 
파라미터: {"name":"작성자","content":"내용","password":"비밀번호"} 
설명: 특정 게시글을 수정합니다. 

 

 

5. 게시물 삭제하기


경로: /board/:id 
헤더: password:"현재 비밀번호" 
설명: 특정 게시글을 삭제합니다. 

 

728x90
반응형

728x90
반응형

  이번 시간에는 간단히 AWS Lambda와 MongoDB를 연동하는 방법에 대해서 알아보도록 하겠습니다. 우리는 추후에 결과적으로 간단한 형태의 게시판 게시글 API를 만들어볼 것입니다. AWS Lambda와 MongoDB 모두 구축이 매우 간단하고, 편리하기 때문에 이 두 서비스를 한꺼번에 이용하는 경우가 많습니다.

 

  따라서 바로  Atlas 회사의 MongoDB를 구축해보도록 하겠습니다. MongoDB는 다음의 포스팅에서 제가 간단히 소개한 바 있습니다. https://ndb796.tistory.com/302

 

Atlas 무료 MongoDB 사용 방법

공부 목적으로 MongoDB를 이용하시는 분들은 Atlas 회사의 MongoDB 서비스를 가장 많이 사용합니다. 이번 포스팅에서는 Atlas 회사의 무료 MongoDB를 사용하는 방법에 대해서 소개하도록 하겠습니다. ▶ Atlas Mon..

ndb796.tistory.com

  위 과정은 매우 금방 따라하실 수 있을 거고, 최종적으로 만들어진 MongoDB의 접속 문자열(String)을 메모장에 기록해 놓은 뒤에 다음의 과정을 따라오시면 됩니다.

 

  더불어 Atlas에서는 공식 AWS Lambda와 Atals MongoDB를 연동하는 예제를 제공하고 있으므로 이를 참고하시면 좋습니다. https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/

 

Best Practices Connecting from AWS Lambda — MongoDB Atlas

Best Practices Connecting from AWS Lambda MongoDB recommends the following best practices to optimize connection times between AWS Lambda and Atlas: Define the MongoDB client connection to the MongoDB server outside the AWS Lambda handler function. This ma

docs.atlas.mongodb.com

 

  가장 먼저 AWS Lambda의 환경 변수(Environment Variables)로 MONGODB_URI을 설정합니다. 이 때 <password> 부분에 자신의 MongoDB 비밀번호를 넣어주셔야 합니다.

 


이제 Lambda 소스코드를 작성합니다.

 

"use strict";

const mongoose = require('mongoose');
const MONGODB_URI = process.env.MONGODB_URI;

module.exports.handler = (event, context, callback) => {
  console.log('event: ', event);
  
  /* Database 설정 및 변수 초기화 */
  mongoose.Promise = global.Promise;
  mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true });
  const db = mongoose.connection;
  db.once('open', function () {
      console.log('Database Connected.');
  });
};

 

  이후에 코드를 [Save]하고 [Test] 버튼을 눌러서 아무렇게나 테스트 설정을 하여 테스트를 해보시면, 아래 쪽에 Cannot find module 'mongoose'라는 메시지가 나오게 됩니다. 이는 Mongoose 라이브러리가 AWS Lambda에 기본적으로 포함되어 있지 않기 때문에 발생하는 문제입니다.

 

 

  따라서 우리가 AWS Lambda에 특정한 라이브러리를 추가하여 사용하고 싶다면, 직접 Node.js 라이브러리를 압축하여 AWS Lambda에 올려야 합니다. 따라서 특정한 폴더에서 npm install mongoose로 MongoDB 라이브러리를 설치합니다.

 


  그리고 nodejs라는 이름의 폴더를 만들고, 그 안에 node_modules 폴더를 둡니다. 그리고 library라는 이름의 폴더를 하나 더 만들어서 nodejs 폴더를 그 안에 둡니다. AWS Lambda에 업로드 하게 되는 압축 파일의 루트 폴더는 nodejs라는 이름을 가진다고 약속되어 있어요. 

 

 

  이후에 library 폴더를 압축합니다. 이후에 그 압축 파일을 AWS Lambda의 Layer에 올립니다. 이 때 Runtime이 자신의 AWS Lambda의 Runtime과 일치하는지 확인할 수 있도록 합니다. (Node.js 8.10 버전인 경우 Layer Runtime도 8.10을 선택합니다.)

 

 

  이제 다시 AWS Lambda로 돌아가서, 우리의 Lambda 함수를 클릭한 뒤에 [Add a layer]를 눌러 레이어를 추가합니다.

 

 

  결과적으로 다음과 같이 업로드한 라이브러리를 선택해서 [Add] 버튼을 누르시면 됩니다.

 

 

  이제 이 상태에서 다시 AWS Lambda를 [Save]하고 [Test]하면 이제 Mongoose를 사용할 수 있게 되는 것을 알 수 있습니다.

 

 

  다만 이 때 위와 같이 정상적으로 DB 접근이 되지 않아서, 'Database Connected.' 라는 로그(Log)가 출력되지 않으면, Atlas에서 DB White List 설정을 확인하실 필요가 있습니다. 혹은 MongoDB 비밀번호가 제대로 입력이 되어 있는지 확인하실 필요가 있습니다.

 

  기본적으로 AWS Lambda의 IP를 Atlas의 White List에 등록해야 사용할 수 있습니다. 하지만 AWS Lambda는 유동 IP 정책을 따르고 있어서, 일일히 모든 IP를 White List에 등록할 수 없습니다. 그래서 흔히 VPC를 이용하여 고정 IP를 할당할 수 있습니다.  다만 이렇게 하면 어렵고, 비용도 많이 발생할 수 있어요. 따라서 그냥 실습 과정에서는 MongoDB에서 모든 IP를 허용할 수 있도록 할 수 있습니다. 따라서 Atlas의 MongoDB에서 [CONNECT]를 눌러 연결 정보를 확인합니다.

 

 

  이후에 다음과 같이 [IP Whitelist tab]을 누릅니다.

 

 

  이후에 다음과 같이 모든 IP 주소에서 접속할 수 있는 Entry를 추가하시면 됩니다.

 

 

  이제 다음과 같이 정상적으로 AWS Lambda를 실행하면, 3초 가량의 시간이 흐른 뒤에 'Database Connected.'라는 메시지가 출력됩니다. 다만 DB 연결과 관련하여 많은 시간이 소요되며, Error Message를 뱉을 수 있습니다.

 

 

  그래서 실제로 코딩을 할 때에는 다음과 같은 방식으로 코드 맨 아래에서 context.done() 함수를 통해 결과를 돌려주며 함수를 마치도록 해야 합니다.

 

"use strict";
const mongoose = require('mongoose');
const MONGODB_URI = process.env.MONGODB_URI; // or Atlas connection string
module.exports.handler = (event, context, callback) => {
  console.log('event: ', event);
  /* Database 설정 및 변수 초기화 */
  mongoose.Promise = global.Promise;
  mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true });
  const db = mongoose.connection;
  db.once('open', function () {
      console.log('Database Connected.');
  });
  context.done(null, { 'statusCode': 200 });
};

 

  이제 결과적으로 200번 응답 코드가 출력되는 것을 알 수 있습니다. 다만 'Database Connected.'라는 로그 메시지는 출력되지 않고, 바로 종료되는 것을 알 수 있습니다. 이제 이러한 구조에서 실제로 DB로부터 데이터를 조회하는 등의 작업을 수행하면, 그 때 정상적으로 동작하게 됩니다. 이상으로 이번 시간에는 AWS Lambda와 MongoDB의 연동 방법에 대한 개괄적인 내용을 다루어 보았습니다.

 

 

728x90
반응형

728x90
반응형

  프로세스 익스플로러(Process Explorer)를 이용하면 어떠한 프로그램이 특정한 폴더나 디바이스에 접근하고 있는지 확인할 수 있습니다. 예를 들어 현재 웹 캠(Web Cam)의 불빛이 들어와서 사용 중이라는 신호가 잡히고 있다고 가정합시다. 이 때 실제로 웹 캠 기능을 이용하고 있지 않다면, 누군가 카메라를 이용해 내 모습을 촬영 중이지 않을까 하는 걱정을 하실 수 있습니다. 이 때, 웹 캠 디바이스에 어떤 프로세스가 접근하고 있는지 확인하는 방법이 있습니다.

 

  프로세스 익스플로러를 이용하면 어떠한 프로세스가 돌아가고 있는지에 대한 자세한 정보를 확인할 수 있습니다.

 

  프로세스 익스플로러 다운로드: https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer

 

 

  프로세스 익스플로러를 다운로드 하면, 다음과 같이 실행 파일을 확인할 수 있습니다.

 

 

※ 프로세스의 속성 확인 ※

 

  프로세스의 속성을 확인할 때는 특정 프로세스를 우클릭하여 [Properties...] 버튼을 누르면 됩니다.

 

 

  특히 [Performance] 탭에서는 해당 프로세스의 구체적인 성능 정보를 확인할 수 있습니다. 해당 프로세스가 몇 번째 우선순위를 가지고 동작하고 있는지, 그리고 가상 메모리 공간은 얼마나 차지하고 있는지 등을 확인할 수 있습니다.

 

 

※ 특정 프로세스 강제 종료하기 ※

 

  특정 프로세스를 강제 종료하려면, 우클릭 이후에 [Kill Process] 버튼을 누르면 됩니다.

 

 

※ 특정한 장치를 사용 중인 프로세스 찾기 ※

 

  특정한 장치를 사용 중인 프로세스를 찾을 때는 먼저 [장치 관리자]를 실행합니다. 저는 다음과 같이 웹 캠(Web Cam)의 속성을 확인해 보았습니다. 이 때 [자세히] - [실제 장치 개체 이름] 탭으로 들어가면, 해당 장치의 구체적인 이름을 얻을 수 있습니다.

 

 

  이제 [Find] 탭에서 [Find Handle or DLL...] 버튼을 누릅니다.

 

 

  이후에 관련 장치를 사용 중인 프로세스를 검색하기 위해, 저는 다음과 같이 검색을 해보았습니다. 그랬더니 웹 캠을 현재 사용 중인 프로세스는 찾지 못했습니다.

 

 

728x90
반응형

728x90
반응형

  공부 목적으로 MongoDB를 이용하시는 분들은 Atlas 회사의 MongoDB 서비스를 가장 많이 사용합니다. 이번 포스팅에서는 Atlas 회사의 무료 MongoDB를 사용하는 방법에 대해서 소개하도록 하겠습니다.

 

  ▶ Atlas MongoDB 사이트: https://www.mongodb.com/cloud/atlas

 

  사이트에 접속하면 다음과 같이 회원가입을 하여 시작하라는 메시지가 등장합니다.

 

 

  무료로 회원가입을 한 이후에 바로 MongoDB 서비스를 이용할 수 있습니다.

 

 

  가입을 하자마자 바로 하나의 데이터베이스를 생성하라고 나옵니다. 그냥 기본 설정 그대로 만들어 주시면 됩니다. 아마 기본적인 클라우드 제공 서비스로는 AWS가 선택되어 있을 겁니다.

 

 

  나머지 설정 또한 기본 설정으로 진행하면 무료로 서비스에 가입할 수 있습니다.

 

 

  클러스터를 생성한 이후에 약간의 시간이 지나면 다음과 같이 초기화가 완료됩니다.

 

 

  이후에 다음과 같이 [CONNECT] 버튼을 눌러서 DB 접속 User 계정을 만들 수 있습니다.

 

 

  다음과 같이 접속을 위한 계정명과 비밀번호를 설정합니다.

 

 

  이후에 실제로 MongoDB에 접속하기 위한 방법을 명시합니다.

 

 

  다음과 같이 Application에서 접속할 수 있도록 [Connect Your Application] 버튼을 누릅니다.

 

 

  결과적으로 다음과 같이 개발 환경에 따른 접속 문자열을 얻을 수 있습니다.

 

 

  또한 지금 이 상태로는 DB에 접속할 수 없습니다. 다시 뒤로 가서 마지막으로 방화벽 설정을 해주세요. 기본적으로 MongoDB에 접속할 수 있는 IP를 직접 입력하는 식으로 연결할 수 있습니다.

 

 

  이제 아까 복사한 접속 문자열(String)을 이용하여 접속할 수 있습니다.

728x90
반응형

728x90
반응형

  크롬(Chrome) 브라우저로 지메일(Gmail)을 이용하시는 분들은 Streak이라는 확장 프로그램을 이용해 내가 보낸 메일을 상대방이 읽었는지 추적할 수 있습니다.

 

Streak 공식 사이트: https://www.streak.com/

 

  저는 이미 Streak을 설치한 상태입니다. 설치가 아직 안 된 분들은 설치 버튼을 눌러서 설치를 진행하시면 됩니다.

 

 

  설치가 된 이후에 크롬 브라우저로 Gmail에 접속하시면 다음과 같이 알림 창이 나오실 거예요. 이처럼 처음 접속할 때는 Streak을 이용하도록 설정하시면 됩니다. 로그인을 하시면 바로 Streak을 이용할 수 있게 됩니다.

 

 

  이제 Streak과 Gmail이 연동되었습니다. 이제 [보낸 편지함]에 [모든 추적된 메일]이라는 항목이 추가되었습니다.

 

 

  이제 Gmail을 이용해 보내는 메일은 자동으로 추적됩니다.

 

 

  결과적으로 메일을 보낸 이후에 [모든 추적된 메일] 탭으로 이동하시면, 다음과 같이 보낸 메일에 대한 추적 여부를 확인할 수 있습니다. 대표적인 정보로는 상대방이 몇 번 읽었는지 알 수 있습니다.

 

 

  실제로, 해당 메일을 상대방이 읽었을 때에는 다음과 같이 언제 상대방이 읽었는지 등에 대한 정보가 출력되는 것을 확인할 수 있습니다.

 

728x90
반응형

728x90
반응형

  갑자기 여러분이 서울에 있다고 해봅시다. 오늘 오후 6시까지 부산에 있는 어떤 기관으로 증빙 서류를 반드시 제출해야 하는데, 지금이 오전 10시다! 그럴 땐 어떻게 해야 할까요?

 

  저는 지금까지 등기 우편만 보내보았는데요. 지금 당장 보내야 할 때는 퀵 서비스라는 것을 이용할 수 있습니다. 저는 이런 게 있다고 들어보기만 했습니다. 실제로 이용해 본 적은 이번이 처음이에요. 그래서 정보를 공유하고자 글을 남깁니다. 어떤 업체를 이용하라고 추천하지는 않을게요. 그냥 구글에 퀵 서비스 업체를 검색하면 많이 나옵니다.

 

 

  예를 들어 제가 강남역 근처에 있고, 부산에 있는 곳으로 우편을 보내야 한다면. 강남역 근처에 있는 퀵 서비스 업체에 연락을 하시면 됩니다. 혹은 몇몇 퀵 서비스 업체는 이미 전국 단위로 퍼져 있고, 콜 센터가 따로 있기 때문에 그냥 전화하면 알아서 가까운 기사님과 연결해줍니다. 따라서 그냥 바로 전화한 뒤에 요건만 말하시면 됩니다.

 

  그리고 일반적으로 서울에서 완전 지방권으로 물건을 보내는 경우에도, 대략 5만원 안으로 해결된다는 특징이 있습니다.

728x90
반응형