이 문서는 NHN Cloud의 Cloud Functions 서비스에서 Java를 사용하여 함수를 개발하는 방법을 상세히 설명합니다.
| 항목 | 값 |
|---|---|
| 지원 버전 | 17, 21 |
| 파일명 | HelloWorld.java |
| Entry Point | example.HelloWorld |
가장 기본적인 함수 형태입니다.
package example;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
public class HelloWorld {
public ResponseEntity<?> call(RequestEntity<?> req) {
return ResponseEntity.ok("Hello World!");
}
}
Java 함수에서는 Spring의 RequestEntity로 HTTP 요청 정보에 접근할 수 있습니다.
package example;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import java.net.URI;
import java.util.Map;
public class HelloWorld {
public ResponseEntity<?> call(RequestEntity<Map<String, Object>> req) {
// HTTP 요청 정보
HttpMethod method = req.getMethod();
URI url = req.getUrl();
HttpHeaders headers = req.getHeaders();
Map<String, Object> body = req.getBody();
// 응답 데이터 구성
String responseBody = String.format(
"Method: %s\nURL: %s\nHeaders: %s\nBody: %s",
method, url, headers, body
);
return ResponseEntity.ok(responseBody);
}
}
Cloud Functions에서 제공하는 Java 템플릿을 다운로드하여 로컬 환경에서 개발할 수 있습니다.
템플릿 다운로드 링크: java.zip
다운로드한 템플릿은 Maven 프로젝트 구조를 따릅니다.
java.zip
├── pom.xml
└── src
└── main
└── java
└── example
└── HelloWorld.java
# 압축 해제
unzip java.zip -d my-java-function
# 작업 디렉터리 이동
cd my-java-function
src/main/java/example/HelloWorld.java 파일을 원하는 로직으로 수정합니다.
// HelloWorld.java - 간단한 수정 예시
package example;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class HelloWorld {
public ResponseEntity<?> call(RequestEntity<Map<String, String>> req) {
try {
// 쿼리 파라미터에서 이름 가져오기
String name = Optional.ofNullable(req.getUrl().getQuery())
.map(q -> q.split("="))
.filter(p -> p.length > 1 && p[0].equals("name"))
.map(p -> p[1])
.orElse("World");
// POST 요청인 경우 body에서 메시지 가져오기
String customMessage = "";
if (req.getMethod() == org.springframework.http.HttpMethod.POST && req.hasBody()) {
customMessage = req.getBody().getOrDefault("message", "");
}
// 응답 데이터 구성
Map<String, String> response = new HashMap<>();
response.put("greeting", "Hello, " + name + "!");
response.put("message", customMessage);
response.put("method", req.getMethod().toString());
response.put("timestamp", ZonedDateTime.now().format(DateTimeFormatter.ISO_INSTANT));
return ResponseEntity.ok(response);
} catch (Exception e) {
Map<String, String> errorResponse = new HashMap<>();
errorResponse.put("error", "Internal Server Error");
errorResponse.put("details", e.getMessage());
return ResponseEntity.status(500).body(errorResponse);
}
}
}
수정된 소스 코드를 다시 ZIP 파일로 압축합니다. pom.xml 파일과 src 디렉터리가 최상위에 포함되도록 압축해야 합니다.
# Windows (PowerShell)
Compress-Archive -Path .\pom.xml, .\src -DestinationPath my-function.zip
# Windows (7-Zip 사용 시)
7z a my-function.zip pom.xml src
# macOS/Linux
zip -r my-function.zip pom.xml src
# 모든 파일 포함(불필요한 파일 제외)
zip -r my-function.zip . -x "*.git*" "target/*" "*.log"
my-function.zip 파일을 업로드합니다.pom.xml이 포함된 ZIP 파일을 업로드해야 합니다.pom.xml과 src 디렉터리가 위치해야 합니다.target 디렉터리, .git 디렉터리 등 불필요한 파일은 포함하지 마세요.package example;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class GetHandler {
public ResponseEntity<?> call(RequestEntity<Void> req) {
if (req.getMethod() != org.springframework.http.HttpMethod.GET) {
return ResponseEntity.status(405).body("Method Not Allowed");
}
// 쿼리 파라미터 가져오기
String name = Optional.ofNullable(req.getUrl().getQuery())
.map(q -> URLDecoder.decode(q, StandardCharsets.UTF_8))
.map(q -> q.split("=")[1])
.orElse("World");
Map<String, String> response = new HashMap<>();
response.put("message", "Hello, " + name + "!");
response.put("timestamp", Instant.now().toString());
response.put("method", "GET");
return ResponseEntity.ok(response);
}
}
package example;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class PostHandler {
public ResponseEntity<?> call(RequestEntity<Map<String, String>> req) {
if (req.getMethod() != org.springframework.http.HttpMethod.POST) {
return ResponseEntity.status(405).body("Method Not Allowed");
}
if (!req.hasBody()) {
return ResponseEntity.badRequest().body("Request body is missing");
}
Map<String, String> requestBody = req.getBody();
String name = requestBody.get("name");
if (name == null || name.isEmpty()) {
return ResponseEntity.badRequest().body("Missing required field: name");
}
// 처리 로직
Map<String, String> response = new HashMap<>();
response.put("id", UUID.randomUUID().toString().substring(0, 8));
response.put("name", name);
response.put("email", requestBody.getOrDefault("email", "not provided"));
response.put("processed_at", Instant.now().toString());
return ResponseEntity.status(201).body(response);
}
}
pom.xml)의존성 관리를 위해 pom.xml 파일을 수정합니다. dependencies 섹션에 필요한 라이브러리를 추가하면, 함수 업로드 시 Cloud Functions가 자동으로 의존성을 다운로드하여 빌드에 포함합니다.
<dependencies>
<!-- 기본 Spring Boot 의존성(Jackson 포함) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.3.2</version>
<scope>provided</scope>
</dependency>
<!-- Apache Commons Lang3 라이브러리 추가 예시 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
package example;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.Map;
public class StringUtilsHandler {
public ResponseEntity<?> call(RequestEntity<Map<String, String>> req) {
if (!req.hasBody()) {
return ResponseEntity.badRequest().body("Request body is missing");
}
String originalText = req.getBody().get("text");
if (StringUtils.isEmpty(originalText)) {
return ResponseEntity.badRequest().body("Body must contain a 'text' field.");
}
// Apache Commons Lang3 StringUtils를 사용한 문자열 조작
Map<String, Object> response = new HashMap<>();
response.put("original", originalText);
response.put("reversed", StringUtils.reverse(originalText));
response.put("isAlphanumeric", StringUtils.isAlphanumeric(originalText));
response.put("wordCount", StringUtils.split(originalText, ' ').length);
return ResponseEntity.ok(response);
}
}
함수에 등록한 환경 변수는 System.getProperty로 접근할 수 있습니다. 환경 변수는 함수 생성 및 수정의 코드 작성 단계에서 등록합니다. 자세한 내용은 콘솔 사용 가이드를 참고하세요.
참고
Java 런타임에서는 환경 변수가 OS 환경 변수가 아닌 JVM 시스템 프로퍼티로 주입됩니다. 따라서 System.getenv가 아닌 System.getProperty로 읽어야 하며, System.getenv를 사용하면 값이 조회되지 않습니다.
package example;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.Map;
public class HelloWorld {
public ResponseEntity<?> call(RequestEntity<?> req) {
// 환경 변수 읽기(Java는 시스템 프로퍼티로 주입되므로 getProperty 사용)
String dbHost = System.getProperty("DB_HOST");
String apiKey = System.getProperty("API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
Map<String, String> error = new HashMap<>();
error.put("error", "API_KEY is not set");
return ResponseEntity.status(500).body(error);
}
Map<String, String> response = new HashMap<>();
response.put("dbHost", dbHost);
return ResponseEntity.ok(response);
}
}
참고
보안상 다음 키·접두사는 환경 변수로 등록할 수 없습니다.
공통(모든 런타임)
PATH, IFS, HOME, BASH_ENV, ENV, SHELLOPTSLD_PRELOAD, LD_LIBRARY_PATH, LD_AUDITGCONV_PATH, LOCPATH, HOSTALIASESHTTP_PROXY, HTTPS_PROXY, ALL_PROXY(소문자 포함)SSL_CERT_FILE, SSL_CERT_DIR, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLERUNTIME_PORT, USERFUNCVOL, WSGI_FRAMEWORK, SENTRY_DSN, SENTRY_RELEASE, TIMEOUT, BODY_PARSER_LIMITLD_, DYLD_, KUBERNETES_, FISSION_, CORECLR_, COMPLUS_, DOTNET_, ASPNETCORE_, PYTHON, NODE_, RUBYJava
JAVA_TOOL_OPTIONS, _JAVA_OPTIONS, JDK_JAVA_OPTIONS, CLASSPATH패키지명.클래스명을 Entry Point로 사용합니다.
exampleHelloWorldexample.HelloWorld중요
함수 역할을 하는 메서드는 public ResponseEntity<?> call(RequestEntity<?> req) 시그니처를 가져야 합니다.
src/main/java 디렉터리 구조를 유지해야 합니다.