자주 잊어버려서 메모

 

nginx location 기본 문법
 - https://lahuman.github.io/nginx_location_options/
-----

# 정확하게 일치 
location = / {
    [ configuration A ]
}

# 지정한 패턴으로 시작
location / {
    [ configuration B ]
}

# 지정한 패턴으로 시작
location /documents/ {
    [ configuration C ]
}

# 지정한 패턴으로 시작 패턴이 일치 하면 다른 패턴 탐색 중지( 정규식 아님 )
location ^~ /images/ {
    [ configuration D ]
}

# 정규식 표현 일치 - 대소문자 구분
location ~ \.(gif|jpg|jpeg)$ {
    [ configuration E ]
}

# 정규식 표현 일치 - 대소문자 구분 안함
location ~* \.(gif|jpg|jpeg)$ {
    [ configuration F ]
}

AWS 람다 + SNS조합해서 바운스된 이메일 DB구축 -> 서비스에서는 적절하게 로직/유저 메시지 처리해야함

 

관련해서 참고 링크

 - https://docs.aws.amazon.com/ses/latest/dg/notification-contents.html

 - 바운스되는 종류(bounce type): https://docs.aws.amazon.com/ses/latest/dg/notification-contents.html#bounce-types

  => 케이스별로 적절하게 로직처리(예. MailboxFull이면 유저에게 메일함이  꽉 찾는지 확인해서 조치요청 UI)

 

 

추가글 참고
- https://blog.eomsh.com/199

'AWS > ' 카테고리의 다른 글

CloudWatch 인스턴스의 메모리 및 디스크 Matrix 추가  (0) 2016.08.07

자동화를 위해서 애플 앱스토어의 여러 기능을 개발할 수 있습니다.(애플에서 API일부를 제공해줘서)

 

그 중 앱의 IAP 리스트를 조회하는 API샘플입니다.

 - 주의: API는 호출양 제한이 걸려있습니다.(링크 참고)

 

List All In-App Purchases for an App

  - https://developer.apple.com/documentation/appstoreconnectapi/list_all_in-app_purchases_for_an_app

 

java 샘플 소스

 - git 주소: https://github.com/oshnew/spring-boot-ver2-study/commit/b04e4293e983bf7425be17a97aaac6c80f70a26b

import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import sample.apple.AppStoreConnectApi.AppStoreConnectApiJWT;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * 특정 애플 앱의 IAP로 등록된 상품 리스트 조회
 *  - 참고: https://developer.apple.com/documentation/appstoreconnectapi/list_all_in-app_purchases_for_an_app
 *
 * @author
 */
public class ListInAppPurchasesForAnApp {

	public static void main(String[] args) throws IOException {

		final String issuer = "입력필요";
		final String keyIdentifier = "입력필요"; //전달받은 사설키의 키 ID
		final String privateKeyFilePath = "입력필요"; //사설키 경로(제품개발 권한 액세스). 애플 앱스토어 웹 콘솔에서 다운로드 받음. 키에 허용권한(ACCOUNT_HOLDER, ADMIN, APP_MANAGER 중 1가지) 필요

		int expireAfterMinutes = 20; //20분 넘으면 안됨

		AppStoreConnectApiJWT clzJWT = new AppStoreConnectApiJWT();
		final String jwt = clzJWT.makeApiAuthJwt(issuer, keyIdentifier, expireAfterMinutes, privateKeyFilePath); //인증용 JWT생성

		System.out.println("인증용 JWT: " + jwt);

		OkHttpClient client = new OkHttpClient.Builder().readTimeout(4, TimeUnit.SECONDS).connectTimeout(1, TimeUnit.SECONDS).writeTimeout(5, TimeUnit.SECONDS).build();

		final String id = "입력필요"; //애플 앱스토어 웹 콘솔에서 'Apple ID'로 표시되는 부분, 앱의 구분용ID로 추정됨
		String prefixUri = String.format("https://api.appstoreconnect.apple.com/v1/apps/%s/inAppPurchasesV2", id);

		HttpUrl.Builder httpBuilder = HttpUrl.get(prefixUri).newBuilder();
		httpBuilder.addQueryParameter("limit", "200"); //최대 200건까지 API에서 제약됨
		httpBuilder.addQueryParameter("filter[inAppPurchaseType]", "NON_CONSUMABLE"); //비 소모성만 조회하기 필터링

		final String fullUrl = httpBuilder.build().toString();
		System.out.println("fullrUrl: " + fullUrl);

		Request request = new Request.Builder().addHeader("Authorization", "Bearer " + jwt).url(fullUrl).build();
		Response response = client.newCall(request).execute();

		if (response.isSuccessful()) {
			String rtnMsg = response.body().string();
			System.out.println(String.format("성공. 응답 msg => '%s", rtnMsg));
		} else {
			System.out.println(String.format("응답에러(200이 아님). 응답 code:'%s'\n errMsg:'%s'", response.code(), response.body().string()));
		}

	}
}

 

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.tomcat.util.codec.binary.Base64;

import java.io.BufferedReader;
import java.io.FileReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 애플 App Store Connect API에서 사용할 인증용 JWT토큰을 생성
 *
 * @author
 */
public class AppStoreConnectApiJWT {

	/**
	 * 애플 App Store Connect API에서 사용할 인증용 JWT토큰을 생성하는 샘플 소스
	 * - 참고: https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests
	 *
	 * @param issuer Your issuer ID from the API Keys page in App Store Connect; for example, 57246542-96fe-1a63-e053-0824d011072a.
	 * @param keyIdentifier
	 * @param expireAfterMinutes
	 * @param privateKeyFilePath
	 * @return
	 */
	public String makeApiAuthJwt(String issuer, String keyIdentifier, int expireAfterMinutes, String privateKeyFilePath) {

		if (expireAfterMinutes > 20) {
			throw new IllegalArgumentException(
				"expireAfterMinutes는 20보다 작아야합니다. 참고: https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests");
		}

		final String strPrivateKey = readPrivateKeyBody(privateKeyFilePath);
		PrivateKey privateKeyObj = convertToPrivateKeyObj(strPrivateKey);

		Map<String, Object> header = new HashMap<>();
		header.put("alg", SignatureAlgorithm.ES256);
		header.put("kid", keyIdentifier);
		header.put("typ", "JWT");

		final Date now = new Date();

		//주의: 일반적으로 만료시간이 20분보다 크면 애플에서 거부함. https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests#3878467
		LocalDateTime expLdt = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault()).plusMinutes(10);
		Date expiredAt = java.sql.Timestamp.valueOf(expLdt);

		// @formatter:off
		String jwt =  Jwts.builder()
		.setHeader(header)
		.setIssuer(issuer)
		.setAudience("appstoreconnect-v1") //애플 API정의서
		.setIssuedAt(now)  //발행 시간
		.setExpiration(expiredAt) //만료시간
		.signWith(SignatureAlgorithm.ES256, privateKeyObj)
		.compact();

		// @formatter:on

		return jwt;
	}

	private static PrivateKey convertToPrivateKeyObj(String strPrivateKey) {

		try {
			byte[] encodedKey = Base64.decodeBase64(strPrivateKey);
			return KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * private key 내용을 얻어옴
	 *  -  -----BEGIN PRIVATE KEY----- 또는 -----END PRIVATE KEY----- 와 같은 가이드라인 줄은 제외하고 실제 사용하는 부분만 파일에서 가져옴
	 *
	 * @param filePath
	 * @return
	 */
	private String readPrivateKeyBody(String filePath) {

		try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {

			String line;
			StringBuilder sb = new StringBuilder();
			while ((line = br.readLine()) != null) {
				if (line.contains("PRIVATE KEY")) { //guard line은 pass
					continue;
				}
				sb.append(line);
			}

			return sb.toString();

		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}
}

 

java 11 vs 17 성능 관련 자료 정리(작성 중)

 

 

참고 자료

  1. https://www.optaplanner.org/blog/2021/09/15/HowMuchFasterIsJava17.html

headless CMS란

 - https://business.adobe.com/kr/glossary/headless-cms.html

 

best-headless-cms-2022

 - https://www.izooto.com/blog/best-headless-cms-2022

 

Site Generators(비교)
 - https://jamstack.org/generators/


Publii - Static-Site CMS (데스크탑 설치형 CMS 오픈소스)

 - https://news.hada.io/topic?id=7068
 - https://github.com/GetPublii/Publii

서비스를 보호하기 위한 요청 양을 제한하는 몇가지 방법이 있습니다.

그 중 bucket4j를 이용한 방법 몇가지를 간략히 메모해두겠습니다.

 

우선 참고 링크들

 

POC 테스트 목적의 간단히 개발한 코드

 - https://github.com/oshnew/spring-boot-ver2-study/blob/master/src/main/java/com/biz/app/ratelimit/RateLimitTestController.java

 

주의: 러프하게 정리 중인 자료인데 마무리가 안 되어 있어서 잘못된 내용이 있을 수 있습니다.

 

 

----

개발 중에 대기열 시스템이 필요한 경우가 종종 있습니다.

관련 내용을 간략하게 메모 목적으로 정리해둡니다.

 

1. 만들려고 하는 대기열 시스템의 개략적인 내용

  - 좀 된 자료로서 몇가지는 변경이 필요(예. vert.x대신 armeira등)

대기열 시스템 개발 설계 요약

2. 기타 참고할만한 내용들 메모

 - https://fr.slideshare.net/devcatpublications/ndc2019-142692683

 - 내용 중 조금 수정/고려해야할 부분이 있어보임(ex. 대기번호를 매번 보내서 비교를 안해도 되는 방법 고민? 등)

 

 - https://www.cloudflare.com/ko-kr/application-services/products/waiting-room/

Windows환경에서 Rust빌드시 아래와 같은 에러가 발생

the msvc targets depend on the msvc linker but `link.exe` was not found

 

아래의 명령어로 GNU방식으로 변경해준다.(실제 Windows환경이지만 리눅스 GNU개발하는 환경으로 셋팅)

rustup default stable-x86_64-pc-windows-gnu

+ Recent posts