2019년 11월 29일 금요일

[Javascript] Prototype에 대해서 알아봅니다.

[Javascript] Prototype에 대해서 알아봅니다.

Prototype(또는 Prototype Object) 이란?

자바스크립트에서 함수를 만들면 함께 생성되는 객체가 Prototype(또는 Prototype Object)입니다.
그리고 해당 함수의 'prototype'이라는 속성으로 Prototype Object와 연결됩니다.
개인적인 생각으로는 Java에서, class 내부의 static member와 유사하다고 생각합니다.

Prototype의 목적

Prototype Object는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용합니다.
간단한 예시를 들면 아래와 같습니다.
function Car () {}

Car.prototype.wheel = 4;
Car.prototype.sideMirror = 2;

let smallCar  = new Car ();
let bigCar = new Car ();

console.log('smallCar :: wheel count = ', smallCar.wheel);
console.log('smallCar :: side mirror count = ', smallCar.sideMirror);
console.log('bigCar :: wheel count = ', bigCar.wheel);
console.log('bigCar :: side mirror count = ', bigCar.sideMirror);
// [ 출력 ]
// smallCar :: wheel count =  4
// smallCar :: side mirror count =  2
// bigCar :: wheel count =  4
// bigCar :: side mirror count =  2
smallCar와 bigCar는 크기가 다를 뿐 둘다 자동차입니다. 자동차이기 때문에 공통적으로 가지고 있는 속성들이 있는데, 이를 smallCar와 bigCar에 각각 만들어주기보다 Car의 Prototype Object에 만들어두면 메모리에 같은 정보를 중복으로 저장시키지 않기 때문에 메모리 효율적이며, 이런 공통적인 속성의 값을 바꿀때 모든 객체의 값을 바꾸지 않고 Prototype Object에 있는 값만 바꿔주면 되어 편리합니다.

Prototype Object에 접근하기

위의 예시를 토대로 Prototype에 접근하는 방법을 알아보면,
Car.prototype 또는 smallCar.__proto__ / bigCar.__proto__ 를 이용해서 Prototype Object에 접근할 수 있습니다.
즉, {생성자함수}.prototype 또는 {객체}.__proto__ 가 Prototype Object를 가리킵니다.

참고자료

2019년 11월 26일 화요일

[Javascript] 일반함수와 화살표 함수(arrow function)의 차이

[Javascript] 일반함수와 화살표 함수(arrow function)의 차이

일반함수와 화살표 함수(arrow function)의 차이에 대해서 알아봅니다.
ES6 이전에는 함수를 선언하기 위해서 함수선언식, 함수표현식 방식을 사용했습니다.
ES6에서는 새로운 함수 선언 방식으로 화살표 함수가 등장했습니다.
기존의 일반함수들은 function 키워드를 이용해서 함수를 선언하지만, 화살표 함수는 이름처럼 화살표 키워드를 이용합니다.
// 함수표현식
let function1 = function () {
  console.log('함수표현식');
}
// 함수선언식
function function2 () {
  console.log('함수선언식');
}

// 화살표 함수
let function3 = () => {
  console.log('화살표 함수');
}
일반함수와 화살표 함수는 크게 3가지 다른 점이 있습니다.

차이점 1: this

화살표 함수와 기존의 일반함수는 this가 다른 곳을 가리킵니다.
화살표 함수의 this는 바로 상위 스코프의 this와 같습니다. 하지만 기존의 일반함수는 this가 동적으로 바인딩됩니다.
일반함수의 this는 아래와 같습니다.
  • 내부함수, 콜백 함수: 전역 객체(브라우저에서는 window, node에서는 global)
  • 객체의 메소드: 메소드를 소유한 객체 자체
  • 생성자 함수: 생성자로 생성하는 객체

차이점 2: 생성자 함수로 사용 가능 여부

일반함수는 생성자 함수로 사용할 수 있지만, 화살표 함수는 생성자 함수로 사용할 수 없습니다.
화살표 함수는 prototype 프로퍼티를 가지고 있지 않기 때문입니다.

차이점 3: arguments 사용 가능 여부

일반함수에서는 함수가 실행될 때 암묵적으로 arguments 변수가 전달되어 사용할 수 있었지만, 화살표 함수에서는 arguments 변수가 전달되지 않습니다.
화살표 함수가 새로 나왔다고 해서 무조건 모든 상황에 좋은 건 아닌것 같습니다.
필요에 따라서 적절하게 함수를 만들어야겠습니다.

참고자료



[Javascript] var, let, const에 대해서 알아봅니다.

[Javascript] var, let, const에 대해서 알아봅니다

var, let, const는 javascript에서 변수를 선언할 때 사용하는 키워드입니다.

var

ES6 이전에는 변수를 선언할 때 var 키워드를 사용했습니다.
우선, var 키워드는 아래와 같은 특징을 가지고 있습니다.
  • 변수의 중복 선언이 가능하다.
  • var 키워드는 생략이 가능하다.
  • block-scope이 아닌 function-scope이다.
  • 호이스팅(hoisting) 당한다.
/*
 * function-scope과 block-scope의 차이
 */

// function-scope
for (var i = 0; i < 10; i++) {
  console.log(i);
}
console.log('outside i: ', i); // 출력: outside i:  10
// i를 var로 선언 했기 때문에 function-scope를 사용해서 for문에 선언한 i가 정상적으로 값을 가지고 있다.

// block-scope
for (let i = 0; i < 10; i++) {
  console.log(i);
}
console.log('outside i: ', i); // 출력: Uncaught ReferenceError: i is not defined
// i를 let으로 선언하면 block-scope을 사용해서 for문 밖에서는 사용할 수 없다.
/*
 * hoisting (호이스팅) 예시
 */

// 작성 코드
for (var i = 0; i < 10; i++) {
  console.log(i);
}

// 호이스팅 된 코드
var i
for (i = 0; i < 10; i++) {
  console.log(i);
}
위와 같은 특징을 가진 var는 변수의 사용에 대해서 다른 프로그래밍 언어와 다른 동작을 하는 이슈가 있었습니다. 잘 알고 일부러 쓰면 유용하게 사용할 수 있지만, 모르고 사용하면 스크립스 수행 간에 오류가 야기 될 수 있습니다.
오류 예 1. 한번 선언한 변수를 모르고 다시 선언 해서 다른 목적으로 사용할 때, 오류가 나지 않는다.
오류 예 2. 호이스팅으로 인해서, 변수 선언 이전에 변수를 사용하더라도 에러가 발생하지 않는다.
이로 인해서 ES6에서는 변수 선언 키워드로 새롭게 let과 const를 만들었습니다.

let

let은 아래와 같이 var와 다른 특징이 있습니다.
  • 중복 선언 불가.
  • block-scope (* const도 동일)
  • 값의 할당 이전에 선언이 반드시 되어 있어야한다.

const

var의 단점을 피하기 위해서 let 하나만 있어도 될 것 같지만, const도 그것 만의 특징이 있습니다.
  • immutable 변수를 만든다.
  • 재선언, 재할당 모두 불가.
  • 선언과 할당이 반드시 동시에 이뤄져야한다.
이로써, ES6에서는 변수의 선언에 사용하는 키워드가 여러개이므로 목적에 따라서 알맞게 사용하여야하겠습니다.

참고자료



2019년 2월 27일 수요일

[AWS] 프론트엔드 개발자가 혼자 웹어플리케이션 만들기. #4. AWS API Gateway, Lambda, DynamoDB를 이용한 REST API 구현

[AWS] 프론트엔드 개발자가 혼자 웹어플리케이션 만들기. #4. AWS API Gateway, Lambda, DynamoDB를 이용한 REST API 구현


  이번 포스팅에서는 AWS의 Serverless(서버리스) 서비스들을 이용해서 REST API를 만드는 방법을 정리해보려합니다.

  REST API라 하면, API를 호출할 수 있는 endpoint와, 호출과 함께 전달되는 데이터를 가공 및 처리할 수 있는 로직, 그리고 로직에 따라 데이터가 저장되거나 읽혀지는 데이터베이스가 기본 구성이 될 것입니다.
  이와 같은 기본 구성은 AWS Serverless 서비스들로 구현 가능합니다. endpoint를 생성 할 수 있는 API Gateway, 로직을 담당하는 Lambda, 데이터베이스는 DynamoDB가 담당합니다.


1. DynamoDB 테이블 생성

  먼저, 데이터를 저장하기 위한 데이터베이스로 DynamoDB 테이블을 생성합니다.
  DynamoDB는 AWS의 완전관리형 비관계형(NoSQL) 데이터베이스 서비스입니다. DynamoDB는 테이블 생성이 매우 간단하고, 인스턴스를 구동하는 방식이 아니기 때문에 RDS 처럼 인스턴스 스펙을 무엇으로 할지 고민할 필요가 없으며, 목표 처리량을 설정하기만 하면 나머지는 시스템에서 자동으로 처리합니다.

  DynamoDB는 Console에서 테이블만 생성하면 데이터베이스 구축 완성입니다. 우선, DynamoDB 대시보드에서 '테이블 만들기'를 클릭합니다.

(그림) DynamoDB 테이블 만들기


  첫 단계로 테이블 생성을 위한 정보를 입력하는데, '테이블 이름'을 입력하고 '기본키'에서 '파티션 키'의 이름과 타입만 입력해도 됩니다. 필요에 따라서 '정렬 키'를 추가할 수도 있습니다. '파티션 키'만 입력한다면, '파티션 키'만으로 저장된 항목 하나를 식별할 수 있어야합니다. 하지만 '정렬 키'를 추가한다면, '파티션 키'와 '정렬 키'의 쌍으로 저장된 항목 하나를 식별할 수 있습니다.
  입력 후에는 하단에 '생성' 버튼을 클릭합니다.

(그림) 테이블 생성을 위한 정보 입력


  이로써 테이블 생성이 끝났습니다. 테이블이 생성되면, 대시보드 좌측 리스트에 테이블이 나타나며, 테이블을 선택했을 때 우측에 나타나는 탭 중에서 '항목' 탭이 저장된 데이터를 볼 수 있는 탭입니다.

(그림) 테이블 생성 완료


2. Lambda Function 생성

  데이터베이스가 준비되었다면, 해당 데이터베이스에 데이터를 Create, Read, Update, Delete 할 수 있는 로직을 수행할 Lambda Function(함수)를 생성할 차례입니다.

  Lambda 대시보드에서 '함수 생성' 버튼을 클릭해서 Lambda Function 생성 절차를 시작합니다.

(그림) Lambda Function 생성 시작

  Lambda Function을 만드는 옵션은 총 3가지가 있습니다. 첫번째는 코드를 처음부터 새롭게 작성하는 '새로 작성' 옵션, 두번째는 AWS에서 미리 작성해둔 템플릿을 기반으로 코드를 작성하는 '블루프린트' 옵션, 세번째는 AWS 사용자들이 만들고 공유한 템플릿을 기반으로 코드를 작성하는 '서버리스 애플리케이션 리포지토리' 옵션입니다. 세번째 옵션은 최근에 새로 추가되었습니다.
  이번 포스팅에서는 '새로 작성'을 선택하겠습니다. 그런 다음, Function 생성을 위한 기본 정보를 입력합니다. Function의 이름을 입력하고 런타임을 선택하고 역할(Role)을 선택합니다. 이때의 역할은 현재 생성할 Function이 가지는 권한입니다. 만일 이 Function이 DynamoDB에 접근해야한다면 역할에 DynamoDB에 접근할 수 있는 정책(Policy)이 포함되어 있어야합니다.
  정보가 다 입력되면 하단에 '함수 생성'을 클릭합니다

(그림) Lambda Function 생성을 위한 정보 입력


  '함수 생성'을 누르고나면 Function의 코드를 작성할 수 있는 페이지가 나옵니다. 그곳에 원하는 동작을 하는 코드를 작성하면 됩니다. 아래 코드는 런타임이 Node.js 6 일때, DynamoDB에 기본적으로 CRUD하는 코드 예시입니다. 코드의 주석에서 중요 코드를 설명하겠습니다.

'use strict';

// DynamoDB에 접근할 수 있는 객체 생성
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();

// API Gateway를 이용해서 Lambda Function을 호출할 때,
// Query String이나 Request Body의 데이터는 handler 함수의 event 매개변수로 전달되어옵니다.
exports.handler = (event, context, callback) => {

  // handler 함수의 callback 매개변수를 이용해서 Lambda에서 API Gateway로 Response를 전달할 수 있습니다.
  const done = (err, res) => callback(null, {
    statusCode: err ? '400' : '200',
    body: err ? err.message : JSON.stringify(res),
    headers: {
      'Content-Type': 'application/json',
    },
  });

  // event 객체에는 API Gateway로 호출받은 HTTP Reqeust가 어떤 method인지 저장되어 있습니다.
  // 아래 코드는 HTTP method에 따라 DynamoDB에 CRUD하는 작업을 수행하는 부분입니다.
  switch (event.httpMethod) {
    case 'DELETE':
      dynamo.deleteItem(JSON.parse(event.body), done);
      break;
    case 'GET':
      dynamo.scan({ TableName: event.queryStringParameters.TableName }, done);
      break;
    case 'POST':
      dynamo.putItem(JSON.parse(event.body), done);
      break;
    case 'PUT':
      dynamo.updateItem(JSON.parse(event.body), done);
      break;
    default:
      done(new Error(`Unsupported method "${event.httpMethod}"`));
  }
};


  DynamoDB의 라이브러리에 대한 내용은 링크에서 확인가능합니다. Node.js용 DynamoDB SDK 확인은 여기에서 가능합니다. SDK 문서를 보면서 원하는 동작을 찾아가시면 됩니다.

  코드를 다 작성하고 '저장'을 누르면 Lambda도 준비가 끝났습니다.


3. API Gateway 생성

  이번에는 위에서 작성한 Lambda Function을 호출할 수 있는 endpoint를 만들기 위해서 API Gateway에서 API를 생성하는 방법을 정리합니다. API Gateway로 API를 생성하면 https를 지원하는 URL 형식의 endpoint를 받을 수 있습니다. 그래서 이 endpoint를 axios 같은 http request 라이브러리에서 사용할 수 있습니다.

  API Gateway 대시보드에 들어가서 'API 작성'을 클릭하여 API 생성 절차를 시작합니다.

(그림) 'API 작성' 클릭


  API 생성의 첫 단계는 생성할 API의 정보를 입력하는 것입니다. 새 API를 만들기 위해서는 'API 이름'만 필수로 입력하면 됩니다.
  '엔드 포인트 유형'은 링크를 참고해서 서비스에 맞게 적절히 선택하시면 됩니다. 여기서는 기본값인 '지역'을 선택하겠습니다.

(그림) API 생성을 위한 정보 입력


  API가 생성이되면 아래 그림처럼 '리소스' 영역에 Root Path ('/')가 나타납니다. 아래 그림의 /file 이나 /test는 제가 연습삼아 미리 추가한 것이고, 최초 API 생성 후에는 Root Path만 있습니다. 이 Root Path 하위에 여러 리소스들을 생성해서 여러 REST API URL을 만들어냅니다.
  새로운 하위 Path를 만들기 위해서는 Root Path를 선택한 상태에서 '작업' > '리소스 생성'을 선택합니다.
(그림) '리소스 생성' 선택


  리소스 생성하기 위해서는 '리소스 이름'만 입력하면 됩니다. '리소스 경로'는 리소스 이름을 따라 자동으로 입력되므로 새로 입력할 필요는 없습니다. CORS 활성화는 여기서 체크하지 않아도 됩니다. 어차피 나중에 메서드들을 생성하고 새로 활성화해줘야 하기 때문입니다.

(그림) '리소스 이름' 입력


  리소스를 생성하면 새로운 하위 path가 생성된 것입니다. 이제 이 path가 지원하는 http method를 생성해야합니다. 그러기 위해서 생성한 리소스를 선택한 상태에서 '작업' > '메서드 생성'을 선택합니다.

(그림) '메서드 생성' 선택


  생성할 메서드를 선택하고 체크 버튼을 클릭하면, 메서드 생성을 위한 정보를 입력하는 페이지로 이동합니다. 여기서는 위에서 만든 Lambda Function을 호출하기 위해서 '통합 유형''Lambda 함수'로 선택합니다. 그리고 'Lambda 프록시 통합 사용'을 체크하고 Lambda Function을 생성한 리전을 선택하고 생성한 Lambda Function을 선택합니다. 'Lambda 함수' 입력칸에는 텍스트를 입력할 수 있는데 첫글자를 입력하고나면 드랍다운이 나타나서 선택할 수 있습니다.

(그림) 생성할 메서드 선택

(그림) 생성할 메서드로 호출할 Lambda 함수 선택


  메서드 정보를 저장할 때, 권한에 대한 확인창이 나타나는데, '확인'을 눌러주면 됩니다.

(그림) Lambda Function 호출을 위한 권한 추가


  새로운 리소스와 메서드를 생성했다면, 'CORS 활성화'를 해주어야합니다. 그래야 브라우저에서 다른 도메인 간의 호출이 가능해집니다. CORS 활성화 페이지에서는 별도의 설정이 필요한 상황이 아니라면 바로 'CORS 활성화 및 기존의 CORS 헤더 대체' 버튼을 클릭합니다.

(그림) 'CORS 활성화'

(그림) 대체 버튼 클릭

(그림) 재확인 창에서 다시 버큰 클릭


  사실 가장 많이 실수 하는 부분이 이 부분일것 같습니다. API를 생성하고 온갖 설정을 했지만, 현재는 실제 사용할 수 없습니다. 설정이 끝난 API는 'API 배포'를 해야만 실제로 사용할 수 있습니다. 배포를 위해서는 '작업' > 'API 배포'를 선택합니다.

(그림) 'API 배포' 선택


   API 배포는 스테이지(Stage)를 지정해서 배포할 수 있습니다. 스테이지를 이용하면, 개발단계의 API와 운영단계의 API의 호출 URL을 구분할 수 있습니다. 예를 들어서 방금 전에 만든 API를 실제 운영단계에서 사용하기 전에 테스트를 해보고 싶다면, dev 또는 test라는 스테이지에 배포하고 이 스테이지의 호출 URL을 이용해서 미리 테스트 한 다음에 운영단계의 스테이지에 배포합니다.
  아래 그림은 새로운 스테이지를 만드는 화면입니다. '스테이지 이름'만 입력하면 API 배포가 가능하지만, 현재 배포하는 내용이 무엇인지 설명을 쓰는것이 API 관리에는 도움이 됩니다.


(그림) API 배포 


  API를 배포하고나면, 이 API를 호출할 수 있는 호출 URL가 주어집니다. 이 호출 URL과 생성한 리소스의 하위 path를 이용하여 http request를 요청할 수 있습니다. 예를 들어서 호출 URL이 'https://api.test.com/prod' 이고 리소스의 하위 path가 '/content' 라면, http request를 'https://api/test.com/prod/content'로 요청할 수 있습니다.

(그림) 스테이지마다 각각 주어지는 호출 URL



  이로써, API Gateway, Lambda, DynamoDB가 결합된 REST API를 완성했습니다.

2018년 4월 24일 화요일

[AWS] 프론트엔드 개발자가 혼자 웹어플리케이션 만들기. #3. AWS Amplify와 S3를 이용한 이미지 저장

[AWS] 프론트엔드 개발자가 혼자 웹어플리케이션 만들기. #3. AWS Amplify와 S3를 이용한 이미지 저장


  이번 포스팅에서는 웹어플리케이션에서 AWS Amplify를 이용하여 AWS S3에 이미지를 업로드 하는 방법을 정리해보려합니다. 사실 이미지가 아닌 모든 파일이 가능하지만, 이번 시리즈에서 예시로 사용하는 웹어플리케이션인 NameSite에 필요한 기능이 이미지 업로드이기 때문에 이미지 파일을 업로드 하는 방법을 정리해봅시다.


> 1. AWS S3 준비

  가장먼저 준비되야할 것은 이미지 파일이 저장될 S3 버킷입니다. 버킷 생성은 매우 간단한 작업입니다.

  S3 대시보드에서 '버킷 만들기'를 클릭합니다.
(그림) '버킷 만들기' 클릭


  그렇게 하면, 아래와 같이 버킷 생성을 위한 정보를 입력하는 창이 뜨는데, 여기서 '버킷 이름'과 '리전'만 선택한 다음에 '생성'을 클릭합니다. 이름은 모든 AWS 유저의 S3 버킷 이름과 중복될 수 없습니다. 혹시 중복된 이름이라서 사용할 수 없다고 오류가 난다면, 조금 더 자신만의 식별자가 될 수 있는 이름을 사용하시길 권장합니다.

(그림) 버킷 생성을 위한 정보 입력


  이렇게 버킷 생성이 간단히 끝나면, 아래와 같이 생성한 버킷이 S3 대시보드의 버킷 리스트에 나타납니다.

(그림) 버킷 생성 후, 리스트에 버킷이 추가 됨


  지금부터는 방금 생성한 버킷을 웹어플리케이션의 리소스 저장소로 사용하기 위하여, 버킷을 Public으로 설정하고 CORS 설정을 하겠습니다.

  버킷을 Public으로 설정한다는 것은, 버킷에 업로드 된 파일들이 인터넷에 공개적으로 노출 된다는 것을 의미합니다. S3 버킷에 업로드 된 파일에는 각각이 고유한 URL이 생성되는데, 버킷이 Public이라면 익명의 사용자가 파일의 URL로 접근했을 때 파일을 다운로드 받을 수 있습니다. 그래서 AWS에서도 버킷을 Public으로 설정하는 작업은 조심하라고 주의를 주고 있습니다. 하지만, 웹사이트에 노출되야하는 이미지들은 Public이여야 하기 때문에 이번 포스팅에서는 S3 버킷을 Public으로 설정하겠습니다.

  버킷을 Public으로 설정하기 위해서는 아래와 같이 해당 버킷의 '권한' 탭으로 이동합니다. 그리고 하위 버튼 중에 '버킷 정책'을 선택합니다.

(그림) S3 버킷의 Public 설정을 위한 이동


  '버킷 정책'을 선택하면 하단에 정책을 입력할 수 있는 입력칸이 나타납니다. 그곳에 아래 정책을 복사하고, '{버킷 이름}' 부분만 본인의 버킷 이름으로 수정합니다. 아래 정책은 지정한 버킷의 모든 파일을 누구나 접근할 수 있게 허용하는 정책입니다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::{버킷 이름}/*"
        }
    ]
}



  그리고 CORS 설정도 해줍니다. '버킷 정책'과 동일한 '권한' 탭에서 'CORS 구성'을 선택합니다.(권한탭에 '퍼블릿'이라는 이름이 붙었는데, 이는 버킷 정책에서 모든 사용자가 접근할 수 있게 정책을 설정해서 그런것입니다.)

(그림) 'CORS 구성' 선택


  CORS 구성을 선택하면 나타나는 입력칸에 아래 내용을 그대로 복사해서 붙여넣습니다.

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <ExposeHeader>x-amz-server-side-encryption</ExposeHeader>
    <ExposeHeader>x-amz-request-id</ExposeHeader>
    <ExposeHeader>x-amz-id-2</ExposeHeader>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>


  이로써, 이미지 파일이 저장될 S3 버킷 준비는 끝났습니다.


> 2. AWS Amplify 설정

  위에서 이미지 파일을 저장할 S3 버킷을 생성했으니, Javascript에서 이미지 파일을 올려야합니다. 이때, AWS Amplify 라이브러리를 이용하려 합니다.

Amplify 라이브러리에 대한 소개와 Amplify 설정 위치는 이전 포스팅을 참고해주시기 바랍니다.

  AWS S3는 Amplify의 Storage 모듈을 이용해서 사용할 수 있습니다. 그러기 위해서 Amplify에서 Storage 모듈을 이용하기 위한 설정을 해주어야합니다. 그 방법은 아래와 같습니다.

  이전 포스팅에서는 Amplify의 Auth 모듈에 대한 설정을 했는데, 거기에 추가로 Storage 모듈에 대한 설정을 아래와 같이 추가합니다. Storage 모듈을 사용하려면 S3 버킷에 파일을 업로드 할 수 있는 권한을 가진 자격증명이 있어야하기 때문에 반드시 Auth 모듈과 함께 설정되어야합니다.
  그리고 로그인하지 않은 익명의 사용자도 파일을 업로드할 수 있게 하기 위해서는, 'identityPoolId'에 입력하는 자격증명풀은 생성하실 때 또는 수정에서 '인증되지 않은 자격 증명에 대한 액세스 활성화'를 체크하시고 IAM Role(역할)에서 해당 자격증명풀(Identity Pool)의 인증되지 않은 사용자를 위한 역할에 S3에 접근할수 있는 정책(Policy)을 추가시켜주셔야합니다.

  아래의 코드는 Storage 모듈을 사용하기 위한 최소한의 Auth 모듈 설정을 작성했습니다.

Amplify.configure({
  Auth: {
    identityPoolId: 'ap-northeast-0:00000000-0000-0000-0000-000000000000',
    region: '{리전 코드}', // 서울리전: ap-northeast-2
  },
  Storage: {
    bucket: '{버킷 이름}', // S3 버킷 이름
    region: '{리전 코드}', // 버킷을 생성한 리전 코드
  }
})



> 3. AWS Amplify.Stoarge API를 이용해서 S3에 이미지 파일 업로드하기

  Amplify의 Storage 모듈 설정이 끝났으면 Storage API를 이용해서 S3 버킷에 파일을 업로드할 수 있습니다.

  예시로 사용되는 코드는 Vue.js 기반의 코드입니다. 하지만 간단한 코드이기 때문에 다른 프론트엔드 프레임워크를 사용하시는 분들도 쉽게 응용가능하시리라 생각합니다.

  우선 html 코드에서 input태그로 파일을 입력받습니다. 그리고 저는 파일이 선택되었을때, handleOnChangeFileS3() 라는 메소드가 실행되도록 하였습니다.

<input type="file" @change="handleOnChangeFileS3($event)">


  handleOnChangeFileS3() 메소드의 코드는 아래와 같습니다. 단지 javascript에서 file 객체를 획득하고, 업로드할 파일 이름을 정한뒤에 Amplify Storage API로 업로드합니다.

handleOnChangeFileS3 (e) {
  e.preventDefault()

  let inputFile = e.target.files[0]
  let date = new Date()

  this.$s3.put(`test-${date.getTime()}-${inputFile.name}`, inputFile)
  .then(resp => {
    console.log(resp.key)
    // resp.key에 업로드한 파일의 이름이 응답으로 옴.
    //이때, 객체의 URL 주소가 아니라 그냥 파일 이름만 key에 담겨서 옴.
  }).catch(err => {
    console.log(err)
  })
}

  Amplify.Storage.put() 메소드를 이용해서 파일을 업로드하고 나면, 응답 객체에 업로드한 파일의 이름이 응답으로 전달됩니다. 그런데, 이 파일의 이름이 S3 객체의 URL이 아니라 그냥 파일 이름뿐이기 때문에, S3에 업로드된 파일의 URL을 javascript에서 쓰고 싶다면 수동으로 붙여줘야합니다.



  이로써, AWS Amplify를 이용해서 AWS S3에 이미지 파일을 업로드 하는 방법을 정리해보았습니다. 혹여 설명이 부족하다면 댓글로 질문해주시면 빠른 시일내에 답변드리겠습니다.
  또한, 잘못된 부분이 있거나 건의해주실 내용도 댓글로 달아주시면 추가 반영하도록 하겠습니다.

  감사합니다.




2018년 4월 18일 수요일

[AWS] 프론트엔드 개발자가 혼자 웹어플리케이션 만들기. #2. AWS Amplify와 Cognito를 이용한 회원가입, 로그인 기능 구현

[AWS] 프론트엔드 개발자가 혼자 웹어플리케이션 만들기. #2. AWS Amplify와 Cognito를 이용한 회원가입, 로그인 기능 구현


  이번 포스팅에서는 웹어플리케이션에 회원가입, 로그인과 같은 회원 인증 관리 기능을 추가 하기 위하여 AWS Cognito를 이용하는 방법을 정리하겠습니다.

  먼저, AWS Cognito에 대하여 소개하고, Cognito에 새로운 User Pool을 생성하고 자격증명풀까지 생성한 다음에, AWS Amplify를 이용해서 웹어플리케이션에서 Cognito를 사용하는 방법을 소개하겠습니다.


> 1. AWS Cognito는 무엇인가?

  AWS의 Cognito는 손쉽게 회원 인증 관리 기능을 구현할 수 있는 서비스입니다. 쉽게 말하자면, 웹어플리케이션에서 회원가입, 로그인 기능을 구현 할 수 있게 간단히 백엔드 시스템을 만들어 주는 서비스입니다.
  또한, Facebook, Google 및 Amazon과 같은 소셜 자격 증명 공급자와 엔터프라이즈 자격 증명 공급자(SAML 2.0 사용)를 통한 로그인을 지원합니다.

> 2. AWS Cognito에서 UserPool(사용자풀) 생성하기

  AWS Cognito는 User Pool(사용자풀)과 Identity Pool(자격증명풀)을 제공합니다.
  User Pool은 회원 DB(Database)라고 생각하시면 됩니다. 그래서 User Pool에서는 회원가입 시에 어떤 정보를 필수로 입력 받을지, 비밀번호 규칙은 어떻게 될지 등의 설정을 하게 됩니다.

  이제, 웹어플리케이션에 연결시킬 User Pool을 생성하는 방법을 정리해보겠습니다.

  먼저, AWS Console에서 Cognito 서비스를 선택하면 아래와 같이 '사용자 풀 관리'와 '연동 자격 증명 관리'가 있습니다. 여기서 '사용자 풀 관리'를 선택합니다.

(그림) '사용자 풀 관리' 선택

  그러면, 새로운 User Pool을 생성할 수 있는 버튼이 있습니다. '사용자 풀 생성' 버튼을 클릭해서 User Pool을 생성하는 절차를 시작합니다.

(그림) '사용자 풀 생성' 클릭

  본 포스팅에서는 주요 설정에 대해서만 살펴보겠습니다. 여기서 다루지 않는 설정은 '다음 단계'를 클릭해서 넘어가도 괜찮습니다.

  첫번째로, '풀 이름'을 원하는 이름으로 입력하고, '설정을 순서대로 진행'을 선택합니다. '기본값 검토'는 모든 설정을 기본값으로 한 상태에서 마지막 검토 페이지로 바로 이동합니다. '설정을 순서대로 진행'을 선택하면 User Pool 생성 단계를 모두 거치면서 원하는 옵션을 선택할 수 있습니다.
  물론, '기본값 검토'를 선택해도 마지막 검토 페이지에서 특정 설정을 변경할 수 있습니다.

(그림) 풀 이름 입력


  로그인 방법 설정을 선택합니다.
  '사용자 이름'을 선택한다면, 회원 가입 시에 아이디(ID)를 입력받고 그것을 이용해서 로그인을 합니다. '사용자 이름' 하위에 있는 옵션들은 아이디는 아니지만 아이디처럼 입력해서 로그인 할 수 있는 옵션들입니다.
  '이메일 주소 또는 전화 번호'는 회원 가입 시에 아이디를 사용하지 않고 이메일이나 전화번호를 아이디로 사용하는 옵션입니다. 최근에는 많은 사이트에서 이메일을 아이디로 사용하는데, 이 옵션을 사용하면 그렇게 사용할 수 있습니다.

(그림) 로그인 방법 선택


  표준 속성 체크란은 회원이 회원 가입할 때 필수로 입력 받아야하는 속성을 선택하는 옵션입니다. 체크를 한다면 필수로 입력 받아야하지만, 체크를 하지 않는다고해서 사용 못하는 속성은 아닙니다. 체크를 하지 않더라도 회원 가입 시에 입력 받고 User Pool에 저장할 수 있습니다.
  그리고 사용자 지정 속성을 추가해서 기본 옵션 이외에 원하는 사용자 정보를 저장할 수 있습니다.

(그림) 사용자 속성 설정


  암호 규칙은 기본적으로 8자 이상 입력, 숫자 포함, 특수 문자 포함, 대소문자 포함을 권장합니다. 하지만 높은 보안성을 위한 권장 일뿐이라서 원하시는 옵션만 선택하셔도 됩니다.

(그림) 암호 규칙 설정


  이메일 또는 전화 번호(휴대폰 번호)를 입력 받기로 했다면, 각각에 대해서 본인 소유의 이메일 또는 전화 번호가 맞는지 확인을 요청할 수 있습니다.
  옵션에서 확인을 원하는 옵션을 선택합니다.
  만약, 전화 번호를 확인하기로 하였다면, 확인 방법은 문자메세지(SMS)로 인증 번호를 전송하고 이 인증 번호를 웹사이트에서 입력하여 확인하는 것입니다. 그렇기 때문에 Cognito가 SMS을 보낼 수 있는 권한이 있어야하기에 하단에 '역할 생성' 버튼을 눌러서 자동으로 역할이 생성되고 할당되도록 해주어야 합니다.

(그림) 연락처를 이용한 사용자 확인 방법 설정


  이메일이나 휴대폰 번호로 전송되는 메세지를 편집 할 수 있습니다.
  이메일의 경우에는 인증 방법이 2가지가 있는데, '코드' 방식은 SMS와 동일하게 인증번호를 보내고 이를 웹사이트에서 입력하여 확인하는 방법이고, '링크' 방식은 이메일로 확인 링크를 보내서 사용자가 링크를 눌렀을 때, 바로 사용자 확인이 되도록 하는 방식입니다.
  이때 주의할 사항이 한가지 있습니다. 페이지 언어가 한국어일 경우에 확인 링크가 들어가는 부분이 '{##이메일 확인##}'으로 되어 있는데, 기본값임에도 불구하고 나중에 풀생성 하려할때 오류가 발생합니다. 이 부분은 '{##Click Here##}'로 수정해주어야합니다.

(그림) 사용자 확인 메세지 설정

  앱 클라이언트는 생성하고 있는 User Pool을 웹어플리케이션 또는 앱(App)과의 연결점 입니다. 웹어플리케이션 또는 앱에서는 User Pool ID와 앱 클라이언트 ID로 User Pool에 접근할 수 있습니다.
  '앱 클라이언트 추가'를 클릭하면 아래처럼 추가 화면으로 바뀌고, 여기서 '앱 클라이언트 이름'을 입력해줍니다. 그리고 연결하려는 앱이 Javascript를 이용하는 웹어플리케이션이라면 반드시 '클라이언트 보안키 생성' 옵션을 체크해제 해주어야합니다. 그렇지 않으면 오류가 발생한다고 합니다.

(그림) 앱 클라이언트 추가

(그림) 앱 클라이언트 설정


  모든 설정을 완료하고나면 마지막으로 설정값들을 검토하는 페이지가 나타나며, 여기서 '풀 생성'을 클릭하면 User Pool 생성이 완료됩니다.

(그림) User Pool 생성 직전의 검토 단계


  풀 생성이 완료되면 User Pool ID가 부여되며, '앱 클라이언트 설정'에서는 앱 클라이언트 ID를 확인 할 수 있습니다. 이 값들은 나중에 웹어플리케이션에서 User Pool에 접근하기 위하여 Javascript 라이브러리의 설정으로 입력해야합니다.

(그림) User Pool ID 확인

(그림) 앱 클라이언트 ID 확인


  부가적으로, User Pool을 생성할 때 사용자 확인 방법을 이메일 링크 방식을 선택했다면, User Pool 생성 이후에 '앱 통합 > 도메인 이름'에서 서브 도메인을 설정해줘야합니다.
  왜냐하면, 사용자가 이메일에서 링크를 클릭했을 때, 확인 결과를 보여주는 페이지가 필요한데, 여기서 설정한 도메인을 해당 페이지 주소로 사용하기 때문입니다.
  도메인 이름을 설정하지 않는다면, 사용자 가입부터가 되지 않습니다.

(그림) 도메인 이름 설정.
이메일 링크 방식을 선택했다면 필수 작업.


> 3. AWS Cognito에서 Identity Pool(자격증명풀) 생성하기

  AWS Cognito에서 제공하는 두번째 서비스는 Identity Pool(자격증명풀)입니다.
  Identity Pool(자격증명풀)은 User Pool과 연결하여, 연결한 User Pool의 회원에게 임시 자격증명을 부여하여 권한에 맞는 AWS 자원에 접근이 가능하게 합니다.

  Identity Pool을 생성해보겠습니다.
  먼저, Cognito 페이지에서 '연동 자격 증명 관리'를 선택합니다.

(그림) 연동 자격 증명 관리 선택


  만들어 놓은 자격 증명 풀이 없다면, 바로 새로 생성하는 단계로 진입합니다. 그렇지 않다면, '새 자격 증명 풀 만들기'를 선택하여 풀 생성 단계로 진입합니다.

(그림) 새 자격 증명 풀 만들기 클릭


  생성에 필요한 정보는 '자격 증명 풀 이름'과 '인증 공급자'에 입력할 Cognito '사용자 풀 ID', '앱 클라이언트 ID'입니다.
  만약, 로그인 하지 않은 사용자에 대해서도 어느정도 AWS 자원에 접근 가능하게 해주고 싶다면, '인증되지 않은 자격 증명에 대한 액세스 활성화'를 체크하시면 됩니다.

(그림) 자격 증명 풀 생성을 위한 정보 입력


  '풀 생성'을 하려하면 역할 설정을 해주어야합니다. 위에서도 설명했듯이, Identity Pool은 로그인한 회원에게 임시 자격 증명을 부여합니다. 여기서 설정하는 역할 설정은, 임시 자격 증명으로 접근 가능한 AWS 자원을 설정하는 작업입니다.
  이 페이지에서 '허용'을 클릭했을 때, IAM 역할(role)에 새로운 역할이 생성되는데, 이 역할에 할당되어 있는 권한들이 임시 자격 증명을 가진 사용자들에게 부여되는 권한입니다.
  본 시리즈에서는 지금 생성하는 자격 증명으로 S3에 이미지 파일을 업로드하는 권한을 부여할 것이기 때문에, 자격증명풀 생성 이후에, 이 단계에서 생성되는 IAM 역할에 S3 버킷을 관리할 수 있는 정책을 추가해주시기 바랍니다.

(그림) 생성하는 자격 증명 풀이 AWS 자원에 접근할 수 있게 역할 부여


  모든 과정을 완료하면 샘플 코드에서 Identity Pool ID를 확인할 수 있습니다.

(그림) Identity Pool ID 확인


> 4. AWS Amplify를 이용해서 웹어플리케이션에서 Cognito 사용하기

  AWS Amplify는 AWS에서 만들고 있는 Javascript 라이브러리입니다. Amplify는 AWS 뿐만 아니라 다른 클라우드 서비스들을 Javascript에서 쉽게 사용할 수 있게 해주는 라이브러리입니다. 하지만 Amplify가 아직 완성도가 많이 높지는 않기 때문에 AWS에 가장 적합하며, AWS 서비스를 사용하기에도 조금은 미흡한 부분들이 있지만 기본적인 기능들은 아주 쉽게 이용가능합니다.

AWS Amplify 공식 웹사이트 : https://aws.github.io/aws-amplify

  공식 웹사이트를 참고해서 프로젝트에 Amplify를 설치하신 후에, 아래의 설명을 보시면서 웹어플리케이션에서 Cognito를 이용하실 수 있습니다.


  우선, Amplify을 웹어플리케이션에서 사용하기 위해서는 Amplify에 설정을 해주어야 합니다. Cognito를 이용하기 위한 설정은 아래와 같습니다.

  설정을 위해서는 2개의 파일이 필요합니다. 첫번째는, 설정값을 가진 객체를 export하는 파일(aws-export.js)이고, 두번째는, Amplify에 설정값을 적용하고 사용하는 파일(aws-amplify.js)입니다. 파일명은 원하시는 것으로 변경해도 좋습니다.
  이렇게 파일을 나눈 이유는, 설정값들이 공개되면 좋지 않은 AWS 자원의 고유값들이기 때문에 설정값을 가진 파일은 .gitignore에 추가하여 Git Repository에 업로드되어 공개되지 않게 하기 위함입니다.

  Cognito의 정보는 Auth라는 키값으로 설정하여 Amplify에 적용하게 됩니다. 그리고 하위 속성으로 region, identityPoolId, userPoolId, userPoolWebClientId를 입력해야합니다.
  각 속성에 대한 이해를 위해서 그림에 있는 주석을 확인해주시기 바랍니다.
  각 속성에 필요한 값들은 위의 Cognito Pool 생성에서 모두 확인 가능합니다.

(그림) Cognito 사용을 위한 Amplify Auth 설정. (aws-export.js)

  이제, 위에서 설정한 값을 Amplify에 적용해야합니다.
  저는 웹프레임워크로 Vue.js의 서버사이드렌더링(SSR) 프레임워크인 Nuxt.js를 사용합니다. 그래서 Amplify를 Vue의 Plugin으로 설정해서 조금 더 사용하기 쉽게 사용하려 합니다.
  아래 코드는 plugin으로 Amplify.Auth를 사용하는 코드입니다만, 중요한 부분은 위의 설정을 import해와서 Amplify.configure()로 적용시켜줬다는 것입니다. 다른 웹프레임워크를 사용하시더라도 이처럼 설정을 Amplify에 적용하시기만 하면 됩니다.
(그림) Amplify를 Vue.js의 plugin으로 등록

  Amplify.Auth를 Vue.js의 plugin으로 등록하여 vue 인스턴스 속성으로 만들었기 때문에 저는 아래와 같이 Amplify.Auth 기능을 사용할 수 있었습니다. signUp을 포함한 기타 함수들에 대한 정보는 Amplify의 Auth Docs에서 확인하실 수 있습니다.

(그림) Amplify.Auth를 이용한 Cognito 회원가입

  참고사항으로, Amplify.Auth를 이용하여 로그인(signIn) 함수를 수행했을때, Access Token등이 자동으로 로컬 브라우저의 특정 Storage에 저장됩니다. 만약, Storage를 쿠키로 설정했다면 로그인 후에 Cognito로부터 응답으로 오는 각종 Token이 쿠키에 저장됩니다.


  여기까지해서, AWS Cognito에서 User Pool과 Identity Pool을 생성하고, AWS Amplify를 이용해서 javascript에서 Cognito를 사용하는 방법을 정리해보았습니다.
  부족한 부분, 잘못 된 부분, 궁금한 부분은 언제나 댓글 또는 메일을 보내주시면 피드백 드리도록 하겠습니다.
  긴 글 읽어주셔서 감사합니다.