웹 액션 실행하기

웹 액션이란 웹 기반의 애플리케이션을 빠르게 구축할 수 있도록 도와주는 Cloud Functions 액션입니다. 웹 액션을 사용하면 별도의 Cloud Functions 인증 키 없이 누구나 웹 애플리케이션에 접근 가능한 백엔드 로직을 만들 수 있습니다.

만약 별도의 인증 로직이 필요하다면 원하는 인증 로직을 직접 구현하여 이용하거나, API Gateway에서 API Key와 IAM 인증을 사용할 수 있습니다.

웹 액션 생성

아래의 예제는 demo 패키지 안에 hello라는 이름으로 웹 액션을 만드는 예제입니다.

먼저 아래와 같은 코드로 hello라는 자바스크립트 코드를 만듭니다.

hello nodejs Action 코드의 예

function main({name}) {
  var msg = 'you did not tell me who you are.';
  if (name) {
    msg = `hello ${name}!`
  }
  return {body: `<html><body><h3>${msg}</h3></body></html>`}
}

이후 demo 이름을 가지는 패키지를 생성합니다.

여러분이 현재 guest 네임스페이스를 사용한다고 가정하였기 때문에 액션의 경로가 /guest/로 시작하였지만, 사용하시는 네임스페이스에 맞게 변경해주시면 됩니다.

옵션과 사용할 경우 생성되는 액션은 별도의 인증 없이 REST 인터페이스를 통해 접근할 수 있는 상태가 됩니다.

웹 액션 실행

웹 액션은 일반 Action과 달리 여러 형태의 출력을 지원하기 때문에 API Gateway를 통해 생성되는 URL에 Type을 명시할 수 있는 구간이 추가됩니다. type 값은 명시적으로 추가해야 하며 URL 파라미터는 Type 뒤에 ?name=name1 형태로 추가할 수 있습니다.

생성된 Web 속성 Action 읠 API Gateway URL의 예

https://l2lwhxikt4.apigw.ntruss.com/api/dev/ZGUVjLH0ZT/{type+}

위의 URL을 풀어 보면 다음과 같은 형태로 구성된다.

https://{product_id}.apigw.ntruss.com/{api_name}/{stage_name}/{resource_name}/{type+}
  • product_id : API Gateway에 생성한 product ID입니다.
  • api_name : API Gateway에 생성한 api 이름입니다.
  • stage_name: API Gateway에 생성하고 배포한 stage 이름입니다.
  • resource_name : API Gateway에 생성한 resource 이름입니다. Cloud functions에서는 다른 리소스를 덮어써서 생기는 문제를 방지하고자 resource 이름은 랜덤하게 생성합니다.
  • type+ : 요청 응답을 어떤 형태로 구성할지에 대한 Type 값입니다. 현재 /json, /http, /svg, /html,/text 등이 지원 가능합니다.

Curl

$ curl https://l2lwhxikt4.apigw.ntruss.com/api/dev/ZGUVjLH0ZT/{type+}

웹 액션 사용 예제

이 예제는 HTTP redirect를 수행하는 웹 액션 예제입니다.

function main() {
  return {
    headers: { location: 'http://NCP-CloudFunctions.com' },
    statusCode: 302
  }
}

이 예제는 헤더에 쿠키를 설정하는 예제입니다.

function main() {
  return {
    headers: {
      'Set-Cookie': 'UserID=Jane; Max-Age=3600; Version=',
      'Content-Type': 'text/html'
    },
    statusCode: 200,
    body: '<html><body><h3>hello</h3></body></html>' }
}

이 예제는 여러 쿠키들을 설정하는 예제입니다.

function main() {
  return {
    headers: {
      'Set-Cookie': [
        'UserID=Jane; Max-Age=3600; Version=',
        'SessionID=asdfgh123456; Path = /'
      ],
      'Content-Type': 'text/html'
    },
    statusCode: 200,
    body: '<html><body><h3>hello</h3></body></html>' }
}

이 예제는 콘텐츠 타입을 image/png로 설정하여 base64 문자열을 통해 이미지를 반환하는 예제입니다.

function main() {
    let png = <base 64 encoded string>
    return { headers: { 'Content-Type': 'image/png' },
             statusCode: 200,
             body: png };
}

이 예제는 application/json 타입으로 json을 반환하는 예제입니다.

function main(params) {
    return {
        statusCode: 200,
        headers: { 'Content-Type': 'application/json' },
        body: params
    };
}

HTTP 응답으로 사용되는 기본 Content-Type 값은 application/json이며 본문에는 JSON 값을 입력할 수 있습니다. 기본 Content-Type은 헤더에서 생략될 수 있습니다.

액션이 반환할 수 있는 최대 응답 크기는 제한(Limit)되어 있어, 제한 값을 초과하여 응답을 반환하면 액션이 실패합니다. 큰 객체 파일들은 Cloud Functions 코드로 반환하지 말고 다른 저장소를 이용하시기 바랍니다.

액션으로 HTTP 요청 다루기

웹 액션이 아닌 Cloud Functions의 액션은 기본적으로 인증을 요구하고 반드시 JSON 객체를 반환해야 합니다. 반면, 웹 액션은 인증 없이 실행될 수 있으며 headers, statusCode, body 등 다양한 콘텐츠로 응답하는 HTTP 핸들러를 구현하는 데 사용될 수 있습니다.

웹 액션은 여전히 JSON 객체를 반환해야 하지만 Cloud Functions 시스템의 컨트롤러는 JSON 객체의 최상위 속성으로 다음 중 하나 이상을 포함하면 웹 액션을 다르게 처리합니다.

  1. headers: 키가 헤더 이름이고 값이 문자열, 숫자, 혹은 boolean 값으로 이루어져 있는 JSON 객체입니다. 하나의 헤더로 여러 값들을 보내려면 헤더의 값이 JSON 배열로 이루어져 있어야 합니다.
  2. statusCode: 유효한 HTTP 상태 코드입니다(기본 값은 200 OK이며 body가 비어 있다면 204 No Content로 반환됩니다).
  3. body: 일반 텍스트, JSON 객체 또는 배열인 문자열 또는 base64로 인코딩된 바이너리 데이터가 될 수 있습니다(기본값은 빈 응답입니다).

bodynull인 경우, 빈 문자열 "" 그리고 undefined는 콘텐츠가 없다고 판단합니다.

컨트롤러는 액션에서 지정한 헤더를 요청/응답을 종료할 때 HTTP 클라이언트에 전달합니다. 비슷하게 컨트롤러는 주어진 상태 코드로 응답합니다. 마지막으로, body는 응답의 body로 전달됩니다. 만약 content-type header가 결과의 headers에 정의되어 있지 않다면 body는 본문이 문자열이 아닌 경우에 대해서 application/json으로 해석되며 이외에는 text/html로 해석됩니다.

만약 content-type이 정의되어 있다면, 컨트롤러는 응답이 이진 데이터이거나 일반 텍스트인 경우 필요에 따라 base64 디코더로 문자열을 디코딩합니다. 이때 본문이 올바르게 디코딩되지 않았다면 호출자에게 오류가 반환됩니다.

HTTP Context

모든 웹 액션은 호출되었을 때 다음과 같은 추가 HTTP 요청 정보를 입력 파라미터로 받습니다.

  1. __ow_method(타입: string): 요청의 HTTP 메서드
  2. __ow_headers(타입: map string to string): 요청 헤더
  3. __ow_path(타입: string): the unmatched path of the request (matching stops after consuming the action extension).
  4. __ow_user(타입: string): Cloud Functions의 네임스페이스
  5. __ow_body(타입: string): 요청 본문이며 내용이 바이너리 또는 JSON객체/배열인 경우 base64로 인코딩된 문자열을 반환하며 그렇지 않으며 일반 문자열을 반환.
  6. __ow_query(타입: string): 쿼리 파라미터로부터 파싱되지 않은 문자열

요청은 위에서 언급한 __ow_ 파라미터를 덮어쓰지 않습니다. 만약 덮어쓰려 할 경우 400 Bad Request와 같은 상태를 반환하며 요청이 실패합니다.

__ow_user는 웹 액션이 authentication을 요구하도록 한 경우에만 노출되며 개발자가 직접 인증 정책을 구현할 수 있도록 허용합니다.__ow_query는 오직 웹 액션이 "raw" HTTP 요청을 처리하도록 선택한 경우에만 처리할 수 있습니다. 이 변수는 &로 구분되어 URI로부터 파싱된 쿼리 파라미터가 포함된 문자열입니다. __ow_body 속성은 "raw" HTTP 요청을 처리하거나 요청 엔티티가 JSON 객체 또는 form data가 아닌 경우에 존재합니다.

추가 기능

웹 액션은 아래와 같은 추가 기능을 제공합니다.

  1. Content extensions: 원하는 콘텐츠 유형을 /json, /html, /http, /svg, /text 중 하나로 지정합니다. URI의 액션 이름에 확장자를 추가함으로써 수행되며 /{Web_Action_URL} 액션은 HTTP 응답을 받기 위해 /{Web_Action_URL}/http처럼 사용됩니다. type 이 없을 경우 실행되지 않습니다.
  2. Projecting fields from the result: /http가 아닌 다른 콘텐츠 확장자와 함께 사용됩니다. 액션 이름 이후에 따라오는 경로는 한 개 혹은 여러 레벨의 응답을 투영하여 보여주기 위해 사용됩니다. 예를 들어 /{Web_Action_URL}/html/body와 같은 요청을 하면 {body: "..." } 형태의 딕셔너리를 반환하는 액션의 경우 body 속성을 선택하여 문자열 값을 직접 반환합니다. 이러한 경로는 절대 경로 모델을 따릅니다( XPath 참조).
  3. Query and body parameters as input: 액션은 요청 본문의 파라미터뿐만 아니라 쿼리 파라미터를 받습니다. 이러한 파라미터를 병합할 때 우선순위는 패키지 파라미터, 액션 파라미터, 쿼리 파라미터 순서이며 겹치는 경우 각각의 이전 값들을 순서대로 재정의합니다. 예를 들어 /{Web_Action_URL}/http/?name=Jane와 같은 요청은 입력 파라미터로 {name: "Jane"}를 액션에 전달합니다.
  4. Form data: 표준 application/json 외에도, 웹 액션은 URL 인코딩된 form data application/x-www-form-urlencoded data 역시 입력으로 받을 수 있습니다.
  5. Activation via multiple HTTP verbs: 웹 액션은 HTTP 메서드로부터 실행될 수 있습니다(GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS).
  6. Non JSON body and raw HTTP entity handling: 웹 액션은 JSON 객체 외의 HTTP 요청 body를 처리할 수 있으며 이해하기 힘든 값도 항상 받도록 선택할 수 있습니다(바이너리가 아닌 경우 일반 문자열 또는 base64로 인코딩된 문자열).

아래의 예제는 웹 액션에서 이러한 기능들을 사용할 수 있는 방법을 간략하게 보여줍니다.

function main(params) {
    return { response: params };
}

위의 예제 코드를 사용하여 액션이 받는 인자가 무엇인지 확인하기 위해 전체 객체를 반환하여 확인합니다.

$ curl https://{Action_URL}/json
{
  "response": {
    "__ow_method": "get",
    "__ow_headers": {
      "accept": "*/*",
      "connection": "close",
      "host": "172.17.0.1",
      "user-agent": "curl/7.43.0"
    },
    "__ow_path": ""
  }
}

쿼리 파라미터와 함께 실행합니다.

$ curl https://{Action_URL}/json?name=Jane
{
  "response": {
    "name": "Jane",
    "__ow_method": "get",
    "__ow_headers": {
      "accept": "*/*",
      "connection": "close",
      "host": "172.17.0.1",
      "user-agent": "curl/7.43.0"
    },
    "__ow_path": ""
  }
}

form data와 함께 실행합니다.

$ curl https://{Action_URL}/json -d "name":"Jane"
{
  "response": {
    "name": "Jane",
    "__ow_method": "post",
    "__ow_headers": {
      "accept": "*/*",
      "connection": "close",
      "content-length": "10",
      "content-type": "application/x-www-form-urlencoded",
      "host": "172.17.0.1",
      "user-agent": "curl/7.43.0"
    },
    "__ow_path": ""
  }
}

JSON 객체와 함께 실행합니다.

$ curl https://{Action_URL}/json -H 'Content-Type: application/json' -d '{"name":"Jane"}'
{
  "response": {
    "name": "Jane",
    "__ow_method": "post",
    "__ow_headers": {
      "accept": "*/*",
      "connection": "close",
      "content-length": "15",
      "content-type": "application/json",
      "host": "172.17.0.1",
      "user-agent": "curl/7.43.0"
    },
    "__ow_path": ""
  }
}

단순히 이름을 문자열로 투영(project)합니다. 위에서 언급한 Projecting fields from the result 추가 기능에 대한 사용 예제입니다.

$ curl https://{Action_URL}?name=Jane
Jane

위의 예제들로 통해 쿼리 파라미터, 폼 데이터, 그리고 JSON 객체 본문 모두 직접 접근할 수 있는 액션 입력 파라미터로서 편리하게 처리되는 것을 볼 수 있었습니다.

하지만 웹 액션이 JSON 객체를 엔티티로 받은 경우가 아니거나 요청 엔티티를 직접 처리하는 경우는 해당되지 않습니다. 예를 들어 "text" content-type으로 예제 코드를 통해 엔티티를 전달한 경우 아래와 같이 __ow_body로 인자가 넘어오는 것을 볼 수 있습니다.

$ curl https://{Action_URL} -H 'Content-Type: text/plain' -d "Jane"
{
  "response": {
    "__ow_method": "post",
    "__ow_headers": {
      "accept": "*/*",
      "connection": "close",
      "content-length": "4",
      "content-type": "text/plain",
      "host": "172.17.0.1",
      "user-agent": "curl/7.43.0"
    },
    "__ow_path": "",
    "__ow_body": "Jane"
  }
}

콘텐츠 확장자

콘텐츠 확장자는 액션을 호출할 때 사용됩니다. 확장자를 생략하면 기본적으로 /http를 사용합니다. /json 확장자는 투영 경로(projection path)를 요구하지 않으며 /http 확장자는 이러한 경로를 지원하지 않습니다. 그러나 /html, /svg 그리고 /text 확장자는 투영 경로를 요구합니다. 편리한 사용을 위해 기본 경로는 확장자 이름과 일치한다고 가정합니다. 따라서 웹 액션을 실행하고 /html 응답을 받기 위해 액션은 html이라는 최상위 속성을 포함하는 JSON 객체로 응답해야 합니다.(또는 응답이 명시적으로 주어진 경로에 있어야 합니다.) 즉, {Action_URL}/html{Action_URL}/html/html 예시처럼 html 속성을 명시적으로 투영하는 것과 같습니다. 경로에 사용되는 액션의 완전한 이름은 패키지 이름을 반드시 포함하여야 하며 패키지가 없을 경우 기본값은 default입니다.

파라미터 보호

액션 파라미터는 보호되고 불변하는 속성으로 관리됩니다. 웹 액션이 활성화되면 파라미터들은 자동으로 불변하게 설정됩니다.

{
    "name":"Jane"
}

이런 변경들의 결과로 name 속성은 Jane으로 바인딩되어 있으며 불변하므로 쿼리나 본문 파라미터로 덮어쓸 수 없습니다. 이렇게 함으로써 실수로 또는 의도적으로 값을 변경하려고 하는 쿼리 또는 본문 매개 변수에 대해 액션을 보호하게 됩니다.

Raw HTTP 처리

웹 액션은 들어오는 HTTP 본문을 해석하고 처리하도록 선택할 수 있습니다. JSON 객체를 액션 입력으로 사용 가능한 일급 객체 속성으로 승격시키지 않아도 됩니다(예: args.name vs parsing args.__ow_query). 이것들은 raw-http를 통해 이루어집니다.

앞서 보여드렸던 것처럼 간단한 예제를 통해 name 속성을 쿼리 파라미터와 요청 본문을 JSON 값으로 받는 예제를 'raw' 웹 액션으로 실행해 보겠습니다.

$ curl https://{Action_URL}/json?name=Jane -X POST -H "Content-Type: application/json" -d '{"name":"Jane"}'
{
  "response": {
    "__ow_method": "post",
    "__ow_query": "name=Jane",
    "__ow_body": "eyJuYW1lIjoiSmFuZSJ9",
    "__ow_headers": {
      "accept": "*/*",
      "connection": "close",
      "content-length": "15",
      "content-type": "application/json",
      "host": "172.17.0.1",
      "user-agent": "curl/7.43.0"
    },
    "__ow_path": ""
  }
}

Cloud Functions는 Akka Http 프레임 워크를 사용하여 바이너리와 일반 텍스트 콘텐츠 타입을 판단합니다.

Base64 타입의 바이너리 본문 디코딩

Raw HTTP로 처리할 때, content-type이 바이너리인 경우 __ow_body 콘텐츠는 Base64로 인코딩되어 있을 것입니다. 아래의 함수들은 Node, Python, 그리고 Swift에서 인코딩된 본문을 어떻게 다루는지 보여줍니다.

간단하게 아래의 함수를 저장하고 Raw HTP 웹 액션을 생성하고 웹 작업을 실행해서 테스트할 수 있습니다.

Node

function main(args) {
    decoded = new Buffer(args.__ow_body, 'base64').toString('utf-8')
    return {body: decoded}
}

Python

def main(args):
    try:
        decoded = args['__ow_body'].decode('base64').strip()
        return {"body": decoded}
    except:
        return {"body": "Could not decode body from Base64."}

Swift

extension String {
    func base64Decode() -> String? {
        guard let data = Data(base64Encoded: self) else {
            return nil
        }

        return String(data: data, encoding: .utf8)
    }
}

func main(args: [String:Any]) -> [String:Any] {
    if let body = args["__ow_body"] as? String {
        if let decoded = body.base64Decode() {
            return [ "body" : decoded ]
        }
    }

    return ["body": "Could not decode body from Base64."]
}

NodeJS 함수를 decode Action으로 저장하고 이를 아래의 명령어로 실행해서 테스트할 수 있습니다.

$ curl -k -H "content-type: application" -X POST -d "Decoded body" https://{Action_URL}/json
{
  "body": "Decoded body"
}

Options 요청

기본적으로, OPTIONS 요청은 CORS 헤더가 자동으로 응답 헤더에 추가됩니다. 이러한 헤더들은 모든 origins에 대해 허용되고 options, get, delete, post, put, head 그리고 patch HTTP를 허용합니다.

추가적으로 HTTP 요청에 존재하는 경우 Access-Control-Request-Headers 헤더는 Access-Control-Allow-Headers 헤더로 에코백됩니다. 기본값은 아래와 같이 생성됩니다.

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH
Access-Control-Allow-Headers: Authorization, Content-Type

또는, OPTIONS 요청은 웹 액션에 의해 매뉴얼 하게 처리될 수 있습니다. 이 옵션을 활성화하기 위해 web-custom-options 옵션을 true값으로 활성화시킵니다. 이 기능이 활성화되면, CORS 헤더는 더 이상 자동으로 추가되지 않습니다. 대신 개발자가 추가해야 할 책임이 있으며 프로그래밍으로 헤더를 결정할 수 있습니다.

아래의 예제는 OPTIONS 요청에 대한 커스텀 응답 코드 예제입니다.

function main(params) {
  if (params.__ow_method == "options") {
    return {
      headers: {
        'Access-Control-Allow-Methods': 'OPTIONS, GET',
        'Access-Control-Allow-Origin': 'example.com'
      },
      statusCode: 200
    }
  }
}

위의 함수를 저장하고 명령어를 실행하면 아래와 같은 결과를 볼 수 있습니다.

$ curl https://{Action_URL}/http -kvX OPTIONS
< HTTP/1.1 200 OK
< Server: nginx/1.11.13
< Content-Length: 0
< Connection: keep-alive
< Access-Control-Allow-Methods: OPTIONS, GET
< Access-Control-Allow-Origin: example.com

에러 처리

Cloud Functions 액션이 실패한 경우 두 가지 실패 모드로 처리됩니다. 첫 번째는 application error로 알려져 있으며 catch 예외와 유사합니다. 액션은 error 속성을 가지는 최상위 JSON 객체를 반환합니다. 두 번째는 developer error로, 액션이 실패하고 응답을 만들지 않을 때 발생합니다. uncaught 예외와 유사합니다.

웹 액션에서 컨트롤러는 애플리케이션 에러를 다음과 같이 처리합니다.

  1. 지정된 경로 투영이 있다면 무시되고 컨트롤러는 error 속성을 대신 사용합니다.
  2. 컨트롤러는 action extension에 의해 함축된 내용을 error 속성 값에 적용합니다.

개발자는 웹 액션이 어떻게 사용되는지 알고 그에 따라 오류 메시지를 생성해야 합니다. 예를 들어 .http 확장자와 함께 사용되는 웹 액션은 {error: { statusCode: 400 }와 같은 HTTP 응답을 반환해야 합니다. 그렇게 하지 않으면 확장자의 content-type과 오류 응답 액션의 content-type이 일치하지 않게 됩니다.

시퀀스를 구성하는 컴포넌트는 적절한 에러를 필요할 때 생성할 수 있어야 합니다.

연관 정보 바로가기

아래 가이드에서 연관 정보를 확인할 수 있습니다.

""에 대한 건이 검색되었습니다.

    ""에 대한 검색 결과가 없습니다.

    처리중...