저번편에는 코드를 작성하기전에 준비단계를 거쳤다면 이번편에서는 C#코드상으로 어떻게 인증하고 어떻게 api를 호출하는지 적어보겠다.


구글 Api Nuget 설치

C#의 누겟은 개사기일정도로 너무 편리하다

api연동을위해 누겟으로 설치를 한다

 

//GoogleAPIs
using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;

api를 추가합니다

 

이제 구글시트api의 코드를 불러올 수 있습니다!!

 


Api인증

        private SheetsService _service = null;
        private void DoCredential()
        {
            // 데이터의 수정,추가를 위해서 SheetsService.Scope.Spreadsheets 해준다.
            string[] arr_scope = { SheetsService.Scope.Spreadsheets };

            UserCredential credential;

            // Client 토큰 생성
            using (var stream = new FileStream(_data_client, FileMode.Open, FileAccess.Read))
            {
                credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.FromStream(stream).Secrets,
                    arr_scope,
                    "user",
                    CancellationToken.None,
                    new FileDataStore(_data_token_folder, true)).Result;
            }

            // API 서비스 생성
            _service = new SheetsService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "TimeClock"
            });

            _is_credential = true;
        }

전체코드이다

나도 주워온 코드를 이해하면서 봤고 필요한 부분은 약간의 수정만 거쳤다


// 데이터의 수정,추가를 위해서 SheetsService.Scope.Spreadsheets 해준다.
string[] arr_scope = { SheetsService.Scope.Spreadsheets };

https://googleapis.dev/dotnet/Google.Apis.Sheets.v4/latest/api/Google.Apis.Sheets.v4.SheetsService.Scope.html

 

Class SheetsService.Scope | Google.Apis.Sheets.v4

Class SheetsService.Scope Available OAuth 2.0 scopes for use with the Google Sheets API. Inheritance System.Object SheetsService.Scope Inherited Members System.Object.Equals(System.Object) System.Object.Equals(System.Object, System.Object) System.Object.Ge

googleapis.dev

요기로 들어가면 SheetsService.Scope클래스를 잘 설명해준다

읽기전용 시트, 드라이브까지 설정 등등 몇몇개가 있는데

나는 시트의 수정까지 필요해서 SheetsService.Scope.[Spreadsheets]를 사용했다


UserCredential credential;

// Client 토큰 생성
using (var stream = new FileStream(_data_client, FileMode.Open, FileAccess.Read))
{
                credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.FromStream(stream).Secrets,
                    arr_scope,
                    "user",
                    CancellationToken.None,
                    new FileDataStore(_data_token_folder, true)).Result;
}

//private readonly string _data_client = Environment.CurrentDirectory + "\\Data\\Client\\my_client.json";
//private readonly string _data_token_folder = "Data\\Client";

UserCredential이라는 인증서 변수를 만들고 내용을 채운뒤 토큰을 생성합니다

이전편에 만들었던 클라이언트정보인 json파일이 필요하다

json파일을 가져오고 그 정보를 통해 인증서를 정보를 생성한다.

 

OAuth로 만들지 않고 서비스계정으로 만들면 더 간편하게 진행되는거 같던데 이거 만들땐 잘 몰랐다... 어차피 개인적인 일로 만드는거라 작동여부에만 살펴봤는데 다음에 해당 프로그램을 수정할 일이 생기면 다시 싹다 봐야할거같긴하다

 


private SheetsService _service = null;
// API 서비스 생성
            _service = new SheetsService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "TimeClock"
            });

            _is_credential = true;

아까 생성한 인증서 정보로 _service를 초기화한다

앞으로 시트를 읽거나 수정할 때 _service를 이용한다.

중간에 ApplicationName은 그냥 뭘 적을지 몰라서 내 프로젝트이름을 넣었다


api로 읽고 쓰기

private void SelectData(string str_column_and_row, out IList<IList<Object>> out_data)
{
        if (!_is_credential)
            DoCredential();

        var request = _service.Spreadsheets.Values.Get(_sheet_id, _sheet_name + "!" + str_column_and_row);

        ValueRange response = request.Execute();
        out_data = response.Values;
}

private void InsertData(string str_sheet_range, ref List<object> list_data)
{
        if (!_is_credential)
            DoCredential();

        var valueRange = new ValueRange()
        {
            MajorDimension = "ROWS",                    // ROWS or COLUMNS
            Values = new List<IList<object>> { list_data } // 추가할 데이터
        };

        var update = _service.Spreadsheets.Values.Update(valueRange, _sheet_id, str_sheet_range);
        update.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.RAW;
        update.Execute();
}

SelectData()로 읽고 InsertData()로 불러온다

 

구글시트의 범위표현식?은

['시트이름' ! 셀 : 셀]  처럼 표현하는데 예를 들면

Sheet1!A1:B3 는 시트1의 A1부터 B3까지의 셀인 A1,2,3 B1,2,3의 셀정보를 가져온다는 뜻이고

하나의 셀값만 가져오려면 Sheet!A1이라고만 치면된다

 

변수중 _sheet_id라고 되어있는 것은

1sZ어쩌고 하는 빨간색으로 칠한 곳이 시트id이다.


private void SelectData(string str_column_and_row, out IList<IList<Object>> out_data)
{
        if (!_is_credential)
            DoCredential();

        var request = _service.Spreadsheets.Values.Get(_sheet_id, _sheet_name + "!" + str_column_and_row);

        ValueRange response = request.Execute();
        out_data = response.Values;
}

var request에 가져올 데이터의 범위를 설정후 .Execute()로 값을 가져오고 ValueRange라는 형식으로 리턴한다

 

데이터는 두번이상 불러오기 때문에 변수를 계속 할당하지 않고 미리 IList<IList<Object>> 라는 하나의 변수를 선언해서

데이터를 불러오면 out파라미터로 빼줬다.


private void InsertData(string str_sheet_range, ref List<object> list_data)
{
        if (!_is_credential)
            DoCredential();

        var valueRange = new ValueRange()
        {
            MajorDimension = "ROWS",                    // ROWS or COLUMNS
            Values = new List<IList<object>> { list_data } // 추가할 데이터
        };

        var update = _service.Spreadsheets.Values.Update(valueRange, _sheet_id, str_sheet_range);
        update.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.RAW;
        update.Execute();
}

데이터를 입력할 범위를 설정한다.

MajorDimension은 마우스를 올리면 설명이 길게 나오는데 대충 뭐냐면

입력할 데이터배열이 1,2,3,4라는 4개의 값이 있다면

1 2                                          1 3

3 4 로 넣을 것인지                  2 4 로 넣을 것인지

기준점을 ROWS or COLUMNS으로 설정하는 곳이다 ROWS로 설정하면 행이 우선순위를 가져서 왼쪽처럼 값이 들어간다

 

밑에는 위에 만들었던 데이터입력정보를 토대로 실행하는 곳인데

여기는 그냥 아~ ValueInputOption을 통해서 입력값에도 옵션을 줄 수 있구나~하고 넘어갔다

 


이제 우리는 api를 인증받고 시트를 읽고 쓸수 있게 되었다

다음 편은 api를 통해 출퇴근 프로그램 코드를 적어보겠따

집에서 혼자 이것저것 하려다 보니까 스스로 시간관리가 잘 안되는 것을 느껴서 한번 출퇴근 프로그램을 만들어보기로 했다. 

어차피 이것도 본인이 안하면 쓸모없는거지만 만들어 놓으면 동기부여도 되고 시간이 계속 기록이 되니 실제로 내가 일하는 시간을 시각화할 수 있다는게 장점이라서 일단 만들어보기로 했다.

 

모든 과정은 처음하는 것이라 실수가 있을 수 있다.


구글 Api 인증받기

https://console.cloud.google.com/

프로젝트가 없다면 바로 만들라고 하는데 이미 만들어진 사람들은

좌상단메뉴->IAM 및 관리자->프로젝트 만들기

에서 만들면된다

 

나는 TimeClock이라는 이름으로 프로젝트를 생성했다

아직 api가 없으니 라이브러리에 들어간후 sheet 라고 검색하면 구글시트api가 나오는데 누르고 '사용'버튼을 누르면된다

사용이 맞나?

 

자 이제 api를 사용할 수 있지만 인증된 계정만 사용할 수 있다.

메뉴->API 및 서비스->OAuth 동의 화면에서

쭈르륵 작성하면되는데 나는 이전에 만들어놔서 저렇게 뜬다

ADD USERS에 api를 사용할 유저의 '이메일'을 입력하면 되는데 자기사진의 이메일을 추가하자

 

좀 알아보니까 OAuth가 아니라 서비스계정으로도 되는거 같은데 이 부분은 나중에 시간이 되면 다루어보겠다

 

 

메뉴->API 및 서비스->사용자 인증정보에서 JSON으로 된 인증정보파일을 다운받자

이제 api를 사용할 모든 준비가 되었다.

 


구글 스프레드시트 공유설정

자신이 사용할 구글시트의 공유를 뷰어로 설정해줘야 한다

 

다음 포스팅은 코드를 적어보겠다

구글링해도 안나와서 UUserWidget클래스를 참고했다

 

블루프린트에서 제목에 있는 함수들을 사용하려면 함수탭에서 오버라이드를 눌러서 만들면 된다.

이걸 C++로 옮기고 싶어서 시도해봤는데 OnTouch~()의 함수원형은 이렇게 생겼다

/**
	 * Called when a touchpad touch is ended (finger lifted)
	 * 
	 * @param MyGeometry    The geometry of the widget receiving the event.
	 * @param InTouchEvent	The touch event generated
	 */
UFUNCTION(BlueprintImplementableEvent, BlueprintCosmetic, Category="Touch Input")
FEventReply OnTouchEnded(FGeometry MyGeometry, const FPointerEvent& InTouchEvent);

보아하니 BluprintImplementableEvent로 인해 블루프린트에서 오버라이드를 할 수는 있지만

가상함수(virtual)가 아니라서 c++에서는 오버라이드가 불가능하다.

 

쫌~더 알아보니까 이런함수가 있더라

virtual FReply NativeOnTouchEnded( const FGeometry& InGeometry, const FPointerEvent& InGestureEvent );
FReply UUserWidget::NativeOnTouchEnded( const FGeometry& InGeometry, const FPointerEvent& InGestureEvent )
{
	return OnTouchEnded( InGeometry, InGestureEvent ).NativeReply;
}

같은 OnTouchEnded지만 다른점은 함수명앞에 Native가 붙고, 리턴값이 조금다르고 내가 원하던 가상함수다.

 

고대로 이 함수를 오버라이드를 해보니 잘 작동된다.

오버라이드 결과물

FReply OnTouchMoved( const FGeometry& InGeometry, const FPointerEvent& InGestureEvent ) override;
FReply UMy_UI::OnTouchMoved( const FGeometry& InGeometry, const FPointerEvent& InGestureEvent )
{
	Super::OnTouchMoved(InGeometry, InGestureEvent);
    
    //FReply::Unhandled();
    return FReply::Handled();
}

 

+ Recent posts