[CUDA] 기본 정리

CUDA|2022. 11. 1. 15:02
 

GitHub - NVIDIA/cuda-samples: Samples for CUDA Developers which demonstrates features in CUDA Toolkit

Samples for CUDA Developers which demonstrates features in CUDA Toolkit - GitHub - NVIDIA/cuda-samples: Samples for CUDA Developers which demonstrates features in CUDA Toolkit

github.com

 

1. include 및 개요

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
  • 문법은 C/C++과 거의 동일하지만, kernel  내에서 Class나 math.h내 함수 사용 같은 건 불가능했다. 대신 쿠다에서 지원하는 math 함수 목록이 있다.
  • 과정: Device set → memory allocation 및 copy → add kernel(GPU로 돌릴 함수 등록) → 완료될 때까지 대기 → 연산 결과를 CPU로 memcpy → 메모리 할당 해제
  • cuda library 함수 이름 형식: cuda$(FunctionName)
  • return value: cudaError_t (typedef int)
    • cudaSuccess와 return된 status 값을 비교하여 성공/실패 확인

 

2. code

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <iostream>

#define SIZE 1023
#define BLOCK_CNT 1
#define THREAD_CNT SIZE

bool isCudaError(cudaError_t status, uint8_t* cudaArr, uint8_t* cudaDest)
{
	auto ret = status != cudaSuccess;
	if(ret)
	{
		cudaFree(cudaArr);
		cudaFree(cudaDest);
	}
	return ret;
}

__global__ void my_func(const uint8_t* src, uint8_t* dest)
{
	const unsigned int index = blockIdx.x * THREAD_CNT + threadIdx.x;
	// gpu로 돌릴 코드 작성
	dest[index] = src[0] + src[4] + src[9] + src[index] * 2;
	// ...
}

int cudaTest()
{
	uint8_t arr[SIZE] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, uint8_t dest[SIZE] = {};
	uint8_t *dev_arr = 0, *dev_dest = 0;

	cudaError_t status;

	// device setting
	status = cudaSetDevice(0); 
	if(isCudaError(status)) return status;

	// kernel 내에서 사용할 배열 malloc
	status = cudaMalloc((void**)&dev_arr, SIZE * sizeof(uint8_t));
	if (isCudaError(status)) return status;
	status = cudaMalloc((void**)&dev_dest, SIZE * sizeof(uint8_t));
	if (isCudaError(status)) return status;

	// cpu에서 gpu로 전달해줄 값이 있을 경우 memcpy
	status = cudaMemcpy(dev_arr, arr, SIZE * sizeof(uint8_t), cudaMemcpyHostToDevice);
	if (isCudaError(status)) return status;

	// gpu로 돌릴 함수 등록
	my_func<<<BLOCK_CNT, THREAD_CNT>>> (dev_arr, dev_dest); // 이 부분 문법이 C/C++과 다르다.
	if (isCudaError(cudaGetLastError())) return status;

	// device sync
	// cuda는 원래 비동기식이고,
	// 이 함수를 사용하면 동기가 보장된다고는 봤는데 더 공부가 필요할듯
	status = cudaDeviceSynchronize();
	if (isCudaError(status)) return status;

	// gpu로 연산한 값을 cpu로 memcpy
	status = cudaMemcpy(dest, dev_dest, SIZE * sizeof(uint8_t), cudaMemcpyDeviceToHost);
	if (isCudaError(status)) return status;

	// cudaMalloc으로 할당한 메모리 해제
	cudaFree(dev_arr);
	cudaFree(dev_dest);
    
	return status;
}
    • 예전에 작성했던 코드를 참고해서 손코딩한 코드이므로 안 돌아갈 수도 있다. 대략적인 흐름 참고용. 실제로 작업했던 코드는 https://github.com/temphi20/cuda-test를 참고.
    • gpu(kernel) 코드 동작
      • n개의 block 안에서 m개의 thread로 돈다. 1블럭당 최대 thread 수는 1024개이며, 이 수를 넘을 시 에러를 반환한다.
      • 변하지 않는 값을 전달할 경우 함수 parameter type를 const로 적어주는 편이 효율적이다.
      • 아마 block 순서대로 프로그램이 굴러가는 것 같다. (block 수를 최소화하도록 설계) ← 함수 내 프린트 결과로 추측한 부분이라 사실 확인 필요
      • thread는 병렬로 진행하므로, 연속된 작업일 경우 한 thread 내에서 해결하도록 한다.
      • gpu로 돌아갈 함수 앞에는 __global__을 붙인다. 작성하는 함수는 한 thread 내에서 하는 일에 해당된다.
      • 순차적인 index 접근을 하는 게 아니기 때문에(병렬 처리) 배열 접근시 index 계산은 코드 작성자가 알아서 해줘야 한다. 테스트 시 blockIdx.xthreadIdx.x로 접근했다.
      • 전달하는 변수도 지나치게 많거나 크고 block당 thread 갯수도 많을 경우 메모리 부족인가...싶은 오류가 발생할 수 있는데, 어지간하면 잘 안 생기는 듯. 이 오류가 발생했을 때도 몇 억 개인가 몇 십억 개인가, 한꺼번에 돌렸었다.

 

3. CUDA를 이용한 라이브러리 만들기

 

4. 함수 목록

 

5. 겪었던 에러 모음

  • [100] no cuda-capable device is detected
    • CUDA 지원이 안 되는 경우, 혹은 nvidia driver를 인식하지 못하거나 driver에 문제가 생긴 경우.
    • cuda package 및 driver 간 version이 달라서 생기는 문제일 수도 있다.
  • [804] forward compatibility was attempted on non supported HW
    • 버전 문제로 추측.
    • nvidia driver가 업데이트 실패해서 알림이 뜨고 있었는데, sudo apt-get install -f로 실패한 업데이트를 설치해준 후 재부팅하니까 해결되었다.
  • [700] an illegal memory access was encountered
    • GPU 연산 중 허용되지 않은 메모리에 접근했을 경우 발생.
    • 할당한 배열의 범위 밖에 해당하는 index에 접근했을 때 해당 오류가 발생되었다.
    • 코드 작성만 잘 하면 안 일어나는 오류이므로 꼼꼼한 작성 및 디버깅 필요.

'CUDA' 카테고리의 다른 글

[CUDA] 설치  (0) 2022.11.01

댓글()

[CUDA] 설치

CUDA|2022. 11. 1. 14:09

다운로드 링크는 아래 참조. 하라는대로 하면 깔리기 때문에 크게 어려운 건 없다.

 

CUDA Toolkit 11.7 Downloads

Get the latest feature updates to NVIDIA's proprietary compute stack.

developer.nvidia.com

 

CUDA 지원 그래픽카드 확인은 아래 링크에서. CUDA 11.7은 nvidia-driver-515 이상부터 호환이 된다고 한다.

 

CUDA GPUs - Compute Capability

Explore your GPU compute capability and CUDA-enabled products.

developer.nvidia.com

 

겪었던 에러 및 시행착오, 팁:

  • linux runfile (local) 방식을 선택했을 경우 설치 실패
    • err log 확인 결과: driver 설치 실패
    • 해결: gpu driver를 제외하고 설치, 드라이버는 apt-get으로 따로 설치해주었다.
sudo apt-get install nvidia-driver-$VERSION
sudo reboot
  • driver까지 모두 설치했는데 linux가 driver를 인식하지 못함
    • nvidia driver 설치 후, reboot할 때 MOK 등록을 하지 않아서...
    • Perform MOK management 화면(파란 화면)  [Enroll MOK] → [View key 0] → [Esc] → [Continue] → [OK] → [Reboot] 과정을 거치니 문제 없이 인식됨.
    • 참고했던 링크 목록:
 

우분투 18.04에 nvidia 그래픽 카드 설치

1. 이 사이트 참조 https://linuxconfig.org/how-to-install-the-nvidia-drivers-on-ubuntu-18-04-bionic-beaver-linux 하지만 내 컴퓨터가 UEFI secure boot이 되어...

sa-park.blogspot.com

 

ELRepo | SecureBootKey

ELRepo

elrepo.org

  • 환경변수 추가
    • window: 설치시 자동으로 추가된다.
    • linux: /usr/local/cuda-$(VERSION)/bin에 위치해있다. 이 경로로 등록하면 끝.
  • linux에서 nvidia driver가 제대로 인식되고 있는지 확인하는 간단한 방법
    • nvidia-smi 명령어 사용.
    • 아래처럼 뜨면 제대로 되고 있는 거다.

'CUDA' 카테고리의 다른 글

[CUDA] 기본 정리  (0) 2022.11.01

댓글()

[Firebase] 예약 함수 (Schedule functions) 사용법

공통 개념, 기타 작업|2022. 4. 29. 20:47

Firebase cloudFunctions 관련 내용은 몇 번 봤는데, schedule functions 관련은 자료를 많이 찾지 못해서 겸사겸사 기록용으로...

참고한 건 firebase 공식 문서.

 

함수 예약  |  Firebase Documentation

Join Firebase at Google I/O online May 11-12, 2022. Register now 의견 보내기 함수 예약 지정된 시간에 함수를 실행하도록 예약하려면 functions.pubsub.schedule().onRun()을 사용하세요. 이 간편한 메서드는 Pub/Sub 주제

firebase.google.com

문서에 따르면, 예약 함수(Schedule functions)란 사용자가 정한 일정에 따라 실행되도록 만든 함수를 말한다고 한다. 매일 정오에 함수가 실행되게 한다든지, 매분 실행되게 한다든지, 매달 특정 날짜에 실행되게 한다든지... 등의 작업을 할 수 있다.

이하 Firebase Functions 작업을 위한 기본 환경이 갖춰져 있다는 전제 하에 작성한다.

 

1. 환경 설정

특별히 해줄 환경 설정은 없다. 배포 방법도 Firebase Functions 배포하던 방법대로 하면 되고, 작성 역시 다른 functions와 함께 index.js에 작성하면 된다.

 

2. 함수 format

firebase 공식 문서에 적힌 그대로 사용해도 문제는 없겠지만, 그렇게 작성하면 설정 변경으로 번거로워질 여지가 있어서...

exports.test = functions.region('asia-northeast3')
    .runWith({ timeoutSeconds: 300 }) // timeout
    .pubsub
    .schedule('0 1 * * *') // 실행 주기
    .timeZone('Asia/Seoul') // timezone
    .onRun((context) => {
        console.log('Hello world');
        return null;
});

pubsub를 제외하면 차례대로 Region, Timeout 설정, Schedule(실행 주기), TimeZone 설정이다.

  • Timeout: 설정된 시간이 지나도록 함수가 종료되지 않으면, Firebase 차원에서 강제로 종료시킨다. 설정해두지 않으면 기본으로 1분이 적용되며, 최대 9분(540초)까지 설정할 수 있다.
  • Schedule: 함수를 실행시킬 작업 일정. 정해진 시간이 되면 onRun에 작성한 함수가 실행된다. 문서 예시대로 every 5 minutes, every 1 hours 등으로도 적을 수 있지만 보다 정확한 시각을 정하려면 아래 링크의 양식을 참고한다. 위 코드에서 정한 작업 일정은 매일 새벽 1시가 된다.
 

크론 작업 일정 구성  |  Cloud Scheduler 문서  |  Google Cloud

의견 보내기 크론 작업 일정 구성 이 페이지에서는 일정 설명을 위해 unix-cron 형식 사용에 대해 간단히 설명합니다. 또한 기존 App Engine 크론 구문을 사용하여 일정을 설명할 수 있습니다. 작업 일

cloud.google.com

 

3. 배포 및 디버그

  • 디버깅은 현재 function ignored because the pubsub emulator does not exist or is not running.에러와 함께 계속 실패하기만 해서 패스...
  • 대신 함수를 배포해가며 테스트했다.
firebase deploy --only functions
firebase hosting:channel:deploy test
firebase deploy --only functions:test,functions:test2

 

4. Google Cloud console

  • 배포한 함수는 Firebase Functions 탭에서 확인할 수 있는데, 예약 함수가 아닌 다른 함수들과 마찬가지로 Google Cloud console의 Cloud Functions 탭에서도 확인할 수 있다.

'공통 개념, 기타 작업' 카테고리의 다른 글

loop break  (0) 2022.03.17
[AWS lamda] response 양식 및 encoding 문제  (0) 2022.03.17

댓글()

[Dart] flutter(dart)에서 dll 불러오기

Flutter|2022. 3. 24. 15:48

flutter에서 사용할 dll 만들기는 이쪽을 참고.

 

[C/C++] dll 작성 (window)

visual studio 2019사용, 환경은 window 64-bit. 처음엔 gcc 쓰려고 했는데 gcc로 컴파일한 걸 다른 언어로 작성한 프로그램에서 실행하자니 오류가 나서...일단 간단하고 편하게 하기 위해 visual studio 2019 기

temphi20.tistory.com

 

크게 어렵지 않다.

1. pub.dev에서 ffi package 다운로드

2. project root folder에 준비된 dll을 추가

3. 아래 소스 코드처럼 library와 함수를 불러온다.

final DynamicLibrary func = DynamicLibrary.open('func.dll');
final int Function(int n) intFunc =
    func.lookup<NativeFunction<Int32 Function(Int32)>>('int_func').asFunction();

NativeFunction class를 살펴보녀 NativeFunction<T extends Function>라고 되어 있는데, T는 dll에서 return하는 type이고 Funtion은 Function(NativeType)의 형식으로 사용해주면 된다. 괄호 안에 들어가는 것은 당연히 dll에 전달할 argument.

Dart코드로 정의하는 함수 타입 역시 NativeFunction에 적은 NativeType과 맞춰 작성해준다.

NativeFunction에는 Dart의 int, double, String 등이 들어갈 수 없으니 주의하자. (어차피 오류나서 못 쓴다.)

C/C++과 Dart 간의 type mapping(Dart type ↔ NativeType class)은 아래 링크를 참고.

 

[Dart] dart:ffi NativeType 관련 정리 및 Pointer 사용법 (추후 추가)

아직 이것저것 실험해보는 중이라 부족할 수 있다. Flutter 2.10.3, window10 64bit 환경에서 테스트. 1. int inteager 관련은 종류가 많다. signed: Int8, Int16, Int32, Int64 unsigned: Uint8, Uint16, Uint32,..

temphi20.tistory.com

 

+위는 윈도우 환경에서 테스트한 코드이며, 플랫폼에 다라 사용 방법이 조금씩 다른 모양이다. 공식 문서 참고.

 

Binding to native code using dart:ffi

To use C code in your Flutter program, use the dart:ffi library (currently in preview).

flutter-ko.dev

 

테스트 중 겪었던 몇 가지 에러 및 주의사항들:

  • Failed to load dynamic library (error code 193): 64bit 환경인데 32bit로 dll을 컴파일해서 생긴 오류였다. 몇 비트인지 확인 제대로 할 것.
  • Failed to load dynamic library (error code 126): dll을 위한 함수 작성시 extern "C"를 사용하지 않아 생긴 오류였다. 같은 C++로 테스트했을 땐 문제가 없었는데, dart로 돌리니 문제가 생겨서 찾기 힘들었음...
  • dll의 내용이 바뀔 경우, hot reload로는 update가 되지 않는다. 껐다가 다시 실행하면 적용된다.

댓글()

[Dart] dart:ffi NativeType 관련 정리 및 Pointer 사용법 (추후 추가)

Flutter|2022. 3. 24. 15:48

아직 이것저것 실험해보는 중이라 부족할 수 있다.

Flutter 2.10.3, window10 64bit 환경에서 테스트.

 

1. int

inteager 관련은 종류가 많다.

  • signed: Int8, Int16, Int32, Int64
  • unsigned: Uint8, Uint16, Uint32, Uint64

ffi/native_type.dart 문서 내에 적힌 설명을 읽어보면, 뒤의 숫자는 각각 몇 bit짜리 정수(C 기준)에 해당하는지 알 수 있다. 테스트로 사용한 dll에서는 int형을 사용하였으므로, Int32로 테스트. bit 수 맞추지 않아도 오류는 나지 않는데...왠지 예상치 못한 동작이 있을 것 같으니 주의하는 편이 좋을 것 같다.

 

2. double, float, bool, void

얘네는 크게 어렵지 않다. 이름도 클래스라 대문자로 시작할 뿐이지, 똑같이 Double, Float, Bool, Void.

 

3. char

Utf8과 Utf16이 있다. Utf8은 char에, Utf16은 wchar_t에 대응하는 모양이다.

char*를 사용하고 싶다면 Pointer<Utf8>을 사용하라고 ffi/lib/src/utf8.dart에 친절하게 적혀 있다. Utf16의 경우에도 마찬가지. 다만 C++의 string class는 무엇을 사용해야 하는지 아직 찾지 못했다.

/// The contents of a native zero-terminated array of UTF-8 code units.
///
/// The Utf8 type itself has no functionality, it's only intended to be used
/// through a `Pointer<Utf8>` representing the entire array. This pointer is
/// the equivalent of a char pointer (`const char*`) in C code.
class Utf8 extends Opaque {}

 

4. 포인터

각종 포인터는 Pointer와 대응한다. 이와 관련한 이야기는 아래에 추가 후술.

메모리 할당 방법 때문에 삽질을 조금 했는데, malloc과 calloc이 구현되어 있었다. package:ffi/src/allocation.dart를 참고하자.

C/C++에서 사용하던 그 malloc 및 calloc과 비슷한 기능을 하고, 비슷한 차이점이 있다.

final Pointer<Int32> int_ptr = calloc.allocate(4);
final Pointer<Utf8> str_ptr = calloc.allocate(32);

이런식으로 할당해주면 된다.

 

5. Pointer class 값 할당

이거때문에 한참 찾아 헤멨는데...

  • NativeType이 숫자(Int32, Double, etc...)일 경우: asTypedList를 이용해 해당 포인터에 대응하는 NativeType List로 바꿔준 후 fillRange 함수를 이용한다.
final Pointer<Int32> ptr = calloc.allocate(16); // malloc.allocate(4);
ptr.asTypedList(8).fillRange(0, 2, 10);

 

  • NativeType이 문자(Utf8, Utf16)일 경우: fillRange가 제공되지 않는다! 대신 다른 방법을 찾았다. 아래와 같다. 물론 이 경우 final Pointer 형식의 사용이 불가능해진다.
Pointer<Utf8> char = calloc.allocate(32);
char = 'test'.toNativeUtf8();

아래처럼 initialize해도 가능하다.

final Pointer<Utf8> ptr = 'hello'.toNativeUtf8();

 

댓글()

[C/C++] dll 작성 (window)

C, C++|2022. 3. 24. 14:50

visual studio 2019사용, 환경은 window 64-bit.

 

처음엔 gcc 쓰려고 했는데 gcc로 컴파일한 걸 다른 언어로 작성한 프로그램에서 실행하자니 오류가 나서...일단 간단하고 편하게 하기 위해  visual studio 2019 기준으로.

 

1. 프로젝트 생성

DLL(동적 연결 라이브러리) 프로젝트를 선택하여 생성한다.

 

2. cpp파일과 h파일 작성

cpp 파일 작성이야 어려울 것 없으니 넘어가고.

문제는 헤더 파일인데...

#pragma once

#ifdef FUNC_EXPORTS
#define FUNC_API __declspec(dllexport)
#else
#define FUNC_API __declspec(dllimport)
#endif

extern "C" FUNC_API int int_func(int i);
extern "C" FUNC_API char* char_func(char* c);
extern "C" FUNC_API void ptr_func(int* ptr);

위와 같이 작성한다. 알아보기 쉽도록 하기 위해 상수 이름은 모두 프로젝트 이름과 통일시켜주었다.

FUNC_EXPORTS는 vs2019로 프로젝트 생성시 자동으로 생성되는 상수 이름인데, 이 이름은 솔루션 탐색기의 [속성] → [C/C++] → [전처리기] → [전처리기 정의]에서 확인할 수 있다. ${name}_EXPORTS라는 이름으로 추가되어 있는 것을 확인할 수 있다.

프로젝트 이름에 따라 상수명이 달라지는 것 같으니 확인 한 번 정도는 해주자. __declspec(dllexport), __declspec(dllimport) 관련은 나중에 따로 공부해서 정리해보는 걸로...

 

3. 빌드

컴파일 시 bit에 주의하자. 아무 생각 없이 build 했다가 다른 언어에서 dll 불러올 때 피 봄.

내 경우에는 기본으로 32bit로 설정이 되어 있어서 64bit로 바꿔주었다.

여기서 바로 바꾸거나...

구성 관리자를 켜서 플랫폼에 맞게 바꿔주자.

이후 build 과정 역시 별 차이 없다. 그냥 [빌드] → [솔루션 빌드] 하면 끝.

 

겪은 에러나 문제:

  • pch.h를 소스에 추가하시겠습니까: 솔루션 탐색기의 [속성] → [C/C++] → [미리 컴파일된 헤더]에서 미리 컴파일된 헤더 항목을 미리 컴파일된 헤더 사용 안 함으로 바꾼다.
  • x64로 바꾼 후 build하니, build한 결과물이 늘 보던 그 폴더가 아니라 project root directory의 x64 안에 들어가 있었다.

댓글()

[Flutter] ipa build Xcode segmentfault 11 문제

Flutter|2022. 3. 22. 19:21

Xcode 업데이트 후 flutter build ipa를 하면 Xcode build 도중 segmentfault 11와 함께 중단되는 현상 발생.

로그를 읽어보면 DKImagePickerController라는 패키지에서 발생하는 문제 같으며... 직접 작성하는 코드에서 일어난 문제가 아니라 그런지 해결 방법이 없다. (file_picker에서 사용하는 패키지 같다.)

 

1. file_picker가 필수가 아닌 프로젝트일 경우

그냥 패키지 사용을 포기한다.

만약 패키지를 지웠는데도 pod install 후 DKImagePickerController가 계속 설치된다면, 아래 두 방법 중 하나로 install한다.

  • 방법1. pod install 옵션
pod install --clean-install

cache를 지우는 옵션이 아니라 cache를 무시하는 옵션이기 때문에, pod install 할 때마다 옵션을 계속 달아줘야 한다.

  • 방법2. cache clear
pod cache clean --all
pod install

 

2. file_picker가 필수인프로젝트일 경우...

Xcode 버전을 낮춰준다. 해당 문제는 13.3에서 발생하고 있으므로, 13.2로 낮춰 실행한 결과 문제 없이 build되었다.

13.2 버전의 Xcode를 다운받은 후, Xcode → [Preferences] → [Locations]로 가서 Command Line Tools에 지정되어 있는 Xcode 버전을 13.2로 바꾼다. 바꾸지 않으면 flutter 명령어는 13.3 버전을 인식한다.

이후 flutter build ipa를 실행하면 해당 문제는 사라진다.

 

+ flutter doctor 명령어로 버전이 바뀌었는지 확인할 수 있다.

댓글()

[Flutter] ipa build 및 browser를 통한 ipa 설치 설정 방법

Flutter|2022. 3. 17. 14:20

apk build에 이은 ipa build 정리.

apple developer 및 기기등록 등이 모두 되어있다고 가정하고 진행한다. (검색하면 많이 나온다.)

 

1. flutter build ipa

반드시 xCode -> [Product] -> [Archive]가 아닌 flutter build ipa 명령어를 사용하여 Runner.xcarchive를 만든다. xCode로 빌드할 경우 이전 빌드 내역 그대로 빌드되니 주의하자.

 

2. Archives

xCode -> [Window] -> [Organizer]로 들어가 Archives를 켠다. 생성된 Runner.xcarchive 파일을 실행하는 방법으로도 켜진다. Runner.xcarchive 파일을 실행하는 방법으로 켜면... 같은 Runner.xcarchive 파일이라도 Archives list에 중복으로 뜨니 이 점 주의.

Creation Date와 Version으로 build하고자 하는 archive가 맞는지 확인하고 선택한 후 [Distribute App]을 누른다.

 

3. Distribute App

- 추출 방법 선택: release test용이라면 Ad Hoc을 선택한다.

- 옵션 선택: Include manifest for over-the-air installation를 반드시 선택해야 한다. App Thinning은 None으로 둬도 큰 문제가 없었다.

- manifest information: 이름과 3개의 url을 입력한다. url은 추후 변경이 가능하므로 아무거나 넣어도 상관 없다.

이후 서명까지 완료하면 자동으로 ipa가 추출된다.

 

4. 추출 후 배포

1. manifest.plist 파일을 수정한다. 형식은 아래와 같다.

item 0의 url은 ipa가 저장된 경로(후술), item 1과 item 2의 url은 앱 아이콘 이미지가 저장된 경로(후술).

ipa 및 이미지는 dropbox를 많이 이용한다. 추출한 ipa 파일(manifest.plist 파일과 같은 경로 혹은 Apps directory 아래에 있다.)과 이미지 파일을 dropbox에 저장한 후 그 링크를 사용하면 된다. 이때 dropbox link는 https://www.dropbox.com/s/...에서 https://dl.dropboxusercontent/s/...로 변경한다. 추후 html 파일에 사용할 link 역시 https://dl.dropboxusercontent/s/...로 변경하여 사용한다.

2. 수정한 manifest.plist를 dropbox에 올린다.

3. 아래와 같은 html 파일을 만든다. link를 이용하는 것이기 때문에 꼭 똑같이 할 필요는 없지 않을까 싶다.

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>IPA 다운로드</title>
 </head>
 <body>
  <ul>
   <li><a href="itms-services://?action=download-manifest&url=https://dl.dropboxusercontent.com/s/.../manifest.plist">다운로드</a></li>
  </ul>
 </body>
</html>

html file에 들어가는 링크는 manifest.plist의 링크이다.

 

호스팅까지 완료하면 된다. dropbox에 html 파일을 올려도 되긴 한데, html 양식으로 보이지 않거나 링크 연결이 안 되는 문제가 생기기도 해서 페이지 하나 호스팅하는 쪽이 더 안정적이고 확실한 것 같다.

 

manifest.plist link를 이용하기 때문에, ipa 업데이트 시 html을 바꿀 필요 없이 dropbox에 같은 이름으로 업로드만 하면 된다. 만약 ipa url이 바뀐다면 manifest.plist만 수정해주면 왼다.

댓글()

loop break

공통 개념, 기타 작업|2022. 3. 17. 13:38

return을 사용할 수 없는 상황에서 이중(혹은 그 이상) for문을 돌릴 때 안쪽 for문 안에서 완전히 멈추고 싶은데 코드가 지저분해지기 싫을 때...

keyword:
for(var i = 0; i < length; i++){
	for(var j = 0; j < length; j++){
		if(j == 1) break keyword;
	}
}

이렇게 keyword를 사용하면 된다. 원하는 keyword를 하나 설정해서 끝낼 for문에 지정하고 break문에 지정한 키워드를 함께 사용하는 방법.

댓글()

[AWS lamda] response 양식 및 encoding 문제

공통 개념, 기타 작업|2022. 3. 17. 13:31

AWS lamda로 api를 작성하는 도중 인코딩 관련 이슈가 생겨 겸사겸사 정리해보았다.

 

1. response 전달 방식

- return JSON;

exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

lamda 첫 생성시 샘플용으로 나오는 코드. 말 그대로 JSON 형식의 response를 return 해주어 전달한다.

 

- context 내 함수

exports.handler = async (event, context) => {
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    context.succeed(response);
};

context.succeed(response), context.fail(error), context.done(error, response)이 있다. succeed는 성공, fail은 실패, done은 어떤 경우에서든 사용이 가능하다.

 

- callback();

exports.handler = async (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    callback(null, response);
};

callback()을 사용하는 방법. context.callbackWaitsForEmptyEventLoop를 false로 설정해주지 않으면 함수가 종료되지 않고 계속 실행중인 느낌이므로 반드시 꺼준다. 자세한 건 공식 문서를 찾아보자.

 

 

AWS Lambda 컨텍스트 객체(Node.js) - AWS Lambda

AWS Lambda 컨텍스트 객체(Node.js) Lambda는 함수를 실행할 때 컨텍스트 객체를 핸들러에 전달합니다. 이 객체는 호출, 함수 및 실행 환경에 관한 정보를 제공하는 메서드 및 속성들을 제공합니다. 컨

docs.aws.amazon.com

 

 

AWS Lambda 함수 핸들러(Node.js) - AWS Lambda

AWS Lambda 함수 핸들러(Node.js) Lambda 함수의 핸들러는 이벤트를 처리하는 함수 코드의 메서드입니다. 함수가 호출되면 Lambda는 핸들러 메서드를 실행합니다. 핸들러가 존재하거나 응답을 반환할 때,

docs.aws.amazon.com

 

2. encoding issue

lambda api에서 찍힌 로그에는 인코딩이 제대로 적용되어 나오는데, response를 받은 후의 로그에는 인코딩이 적용되지 않아 깨지는 issue가 있었다. response 양식을 제대로 지키지 않아 생긴 문제로 추측된다.

encoding 이슈가 생겼을 때 전달한 response는 아래와 같았고,

const response = (code, data) => ({ code: code, data: data });

이후 제대로 동작한 response는 아래와 같았다.

const response = (statuseCode, body) => ({
    statusCode: statusCode,
    body: body
});

다음부터는 샘플 코드를 좀 더 신경 쓰는 걸로...

'공통 개념, 기타 작업' 카테고리의 다른 글

[Firebase] 예약 함수 (Schedule functions) 사용법  (0) 2022.04.29
loop break  (0) 2022.03.17

댓글()

[Flutter] class operator override

Flutter|2021. 12. 17. 15:20

Dart는 class에서 연산자를 재정의할 수 있는 모양이다. Overriding 방법은 다른 함수를 overriding할 때와 비슷하지만, 함수명 대신 operator + 등과 같이 써주면 된다. == 연산자의 경우, argument에 covariant를 붙여준 후 hashCode도 override해줘야만 한다. 아래 예시에서 override toString()은 결과 출력을 위해 추가했다.

 

예시:

class Class {
  final String id;
  final DateTime date;
  final num number;

  const Class({
    this.id = "",
    required this.date,
    this.number = 0,
  });

  @override
  int get hashCode => date.hashCode;

  @override
  bool operator ==(covariant Class other) => date.compareTo(other.date) == 0;

  bool operator >(Class other) => date.compareTo(other.date) > 0;
  bool operator <(Class other) => date.compareTo(other.date) < 0;
  bool operator >=(Class other) => date.compareTo(other.date) >= 0;
  bool operator <=(Class other) => date.compareTo(other.date) <= 0;

  Class operator +(Class other) => Class(
        id: id,
        date: DateTime.now(),
        number: number + other.number,
      );
  Class operator -(Class other) => Class(
        id: id,
        date: DateTime.now(),
        number: number - other.number,
      );
  Class operator -() => Class(
        id: id,
        date: DateTime.now(),
        number: -number,
      );
  Class operator *(Class other) => Class(
        id: id,
        date: DateTime.now(),
        number: number * other.number,
      );
  Class operator /(Class other) => Class(
        id: id,
        date: DateTime.now(),
        number: number / other.number,
      );
  Class operator ~/(Class other) => Class(
        id: id,
        date: DateTime.now(),
        number: number ~/ other.number,
      );
  Class operator %(Class other) => Class(
        id: id,
        date: DateTime.now(),
        number: number % other.number,
      );

  @override
  String toString() {
    return number.toString();
  }
}

 

결과:

void main() {
  Class z = Class(id: "last", date: DateTime(2021, 12, 31), number: 127),
      a = Class(id: "first", date: DateTime(2022, 1, 1), number: 0),
      az = Class(id: "what", date: DateTime(2021, 12, 31), number: 0);

  print([a == a, a == z, a == az, z == az]);
  print([a > a, a > z, a > az, z > az]);
  print([a < a, a < z, a < az, z < az]);
  print([a >= a, a >= z, a >= az, z >= az]);
  print([a <= a, a <= z, a <= az, z <= az]);
  print([a + z, a - z, a * z, a / z, a ~/ z, a % z]);
  print([a += z, a]);
  print([a -= z, a]);
}
[true, false, false, true]
[false, true, true, false]
[false, false, false, false]
[true, true, true, true]
[true, false, false, true]
[127, -127, 0, 0.0, 0, 0]
[127, 127]
[0, 0]
Exited

댓글()

[Flutter] 현재 url 가져오기, 새 창으로 열기

Flutter|2021. 12. 13. 16:23

1. 현재 url 가져오기

- dart:core

final Uri uri = Uri.base;
final String url = Uri.base.toString();

- import 'dart:html'

import 'dart:html' as html;

/// ...

final String url = html.window.location.href;

- import 'dart:js'

import 'dart:js' as js;

/// ...

final String url = js.context['location']['href'];

 

2. 새 창으로 열기

- import 'dart:html'

import 'dart:html' as html;

/// ...

html.window.open(url, 'new tab');

- import 'dart:js'

import 'dart:js' as js;

/// ...

js.context.callMethod('open', [url]);

- import 'package:url_launcher/url_launcher.dart'; (pub.dev)

import 'package:url_launcher/url_launcher.dart';

/// ...

await launch(
	urlParams,
	forceWebView: true,
	forceSafariVC: true,
);

 

※ dart:html과 dart:js는 web only이므로 주의한다.

댓글()