Skip to main content

[WebApp] Protocal22-API 사용예제 (GoLang)

[WebApp] Protocal22-API 사용예제2

시작하기 전에

이 문서는 Dabory OwnerKey API for WebApp을 사용해 main app과 guest app 개발시 필요한 참고 정보를 안내합니다.

 WebApp 사용예제

2. WebApp 사용 예제

📌 GateToken 발급 스케줄러

package main
func main() {
	// 새로운 고루틴(Goroutine) 실행: 백그라운드에서 주기적으로 실행됨
	go func() {
		for {
			// 1. UpdateGateTokenIfNeeded() 함수 호출 (GateToken 발급 및 업데이트)
			err := controllers_func.UpdateGateTokenIfNeeded()
			
			// 2. 만약 에러가 발생하면 로그를 남김
			if err != nil {
				e.ErrLog("UpdateGateTokenIfNeeded Error", err)
			}

			// 3. 2초 동안 대기 후 반복 (즉, 2초마다 실행됨)
			time.Sleep(2 * time.Second)
		}
	}()

	// 주의: main 함수가 종료되면 고루틴도 종료됨
	select {} // main 함수가 종료되지 않도록 무한 대기
}

📌 "OwnerKey"를 활용한 GateToken, BackUrl 반환

Host의 dabory-app/gate-token-get을 호출 -> Host -> main_api (GateToken) 반환


// GateToken을 요청하는 함수
func UpdateGateTokenIfNeeded() error {
	myOwnerKey := "My_OwnerKey" //OwnerKey (GateToken 발급 주체 선정을 위해 사용)

	// GateToken을 요청 (OwnerKey를 사용하여 요청)
	err := locals.GuestGateTokenGets(myOwnerKey)
	if err != nil {
		fmt.Println("GateToken 요청 실패:", err)
		return err
	}

	return nil
}


type AppApi struct {
	ApiUrl    string
	GateToken string
}

var GAppApis [2]AppApi // GateToken, ApiUrl 저장 장소 

func GuestGateTokenGets(myOnwerKey string) error { 
	appType := 0 //Dbupdate
	// 0:keypair, 1:Dbu  // "SsoConnString" Must be Deprecated

	var err error

	if myOnwerKey != "" { 
		// e.LogStr("lskdjqfals", e.LogFuncName()+"; GuestGateTokenOwner with OwnerKey: "+myOnwerKey)
		_, _, err = GuestGateTokenOwner(appType, "https://host_domain_URL.com", myOnwerKey) // app type -> OwnerCode
      // Host Domain URL, OwnerKey를 매개변수로 던짐
		if err != nil {
			return e.ErrLog(e.FuncRun("23rfsr3qrase", e.CurrFuncName()), err)
		}
	} 

	return nil
}

func GuestGateTokenOwner(appType int, pivotUrl string, ownerKey string) (string, string, error) { // HOST URL/dabory-app ->  pivot URL
	var appTypeCode string
	if appType == 0 {
		appTypeCode = "keypair"
	} else if appType == 1 {
		appTypeCode = "dbupdate"
	}

	if GAppApis[appType].GateToken != "" { // 할당되어 있는 경우 (이미 GateToken이 있는 경우)
		// fmt.Println("34092ujfa : "+"Using GateToken in ARRAY to access : ", GAppApis[0].GateToken)
	} else { // (GateToken이 없는 경우 발급 로직)
		fmt.Println("34092ujfa : " + "isn't exist GateToken in ARRAY to access ")

		vGt := &GateTokenGetReq{
			OwnerKey: ownerKey,
		}
		bodyBytes, _ := json.Marshal(vGt)

		frontUrl := pivotUrl + "/dabory-app/gate-token-get" 
		msgBytes, staInt, err := HttpResponseSimplePost("POST", frontUrl, bodyBytes) 
        // Strong -> Host Lalavel로 Http 요청 -> Lalavel -> main_api로 GateToken 요청
		e.LogStr("23rfsr3qr-GateTokenGetReq", e.LogFuncName()+";  Raravel frontUrl : "+frontUrl)
		if err != nil {
			return "", "", e.ErrLog(e.FuncRun("45425fd34sd-The HTTP request "+frontUrl, e.CurrFuncName()), err)
		}

		if staInt != 200 {
			TrackFailure() // 요청 실패 시 실패 기록 추가
			fmt.Println("GateToken 요청 실패:", err, "(최근 1시간 내 실패 횟수:", len(failTimestamps), ")", "반환 Code : ", staInt)

			// 최근 1시간 동안 실패 횟수가 maxFailures(3) 이상이면 프로그램 종료
			if len(failTimestamps) >= maxFailures {
				fmt.Println("1시간 내 GateToken 요청이", maxFailures, "번 실패 -> 스케줄러를 종료")
				os.Exit(1) // 프로그램 종료
			}

			return "", "", errors.New(e.FuncRun("87ty344ra3-Request Fail "+string(msgBytes), e.CurrFuncName()))
		}

		failTimestamps = []int64{} // 성공 시 실패 기록 초기화

		ret := &struct {
			ApiUrl    string 
			GateToken string
		}{}

		if err := json.Unmarshal(msgBytes, ret); err != nil {  // GateToken과 BackendUrl을 ret 구조체에 할당
			return "", "", e.ErrLog(e.FuncRun("45425fd34sd-Json Format "+frontUrl, e.CurrFuncName()), err)
		}
      // 새로 받은 GateToken을 전역 변수에 저장
		GAppApis[appType].ApiUrl = ret.ApiUrl
		GAppApis[appType].GateToken = ret.GateToken
		e.OkLog("Just Added GateToken in ARRAY to access AppType: " + appTypeCode)
		e.OkLog("Just Added GateToken in ARRAY to access AppType: " + ret.GateToken)
		e.OkLog("Just Added GateToken in ARRAY to access AppType: " + ret.ApiUrl) // main_api URL

	}
	return GAppApis[appType].ApiUrl, GAppApis[appType].GateToken, nil
}

📌 발급받은 GateToken을 사용하여 main_api 요청

// 🔹 API 요청을 수행하는 함수
func process() {
	// 1. GateToken과 API URL 가져오기
	apiUrl := locals.GAppApis[0].ApiUrl    // API 요청을 보낼 기본 URL
	gateToken := locals.GAppApis[0].GateToken // API 요청 시 필요한 인증 토큰

	// 2. 요청할 API 엔드포인트 설정 (credit-page 요청)
	mainApiURL := apiUrl + "/credit-page"

	// 3. 요청 바디 구성 (전달할 데이터)
	requestBody := map[string]interface{}{
		"PageVars": map[string]interface{}{
			"Fields":     "credit_no", 
			"Limit":      1,          
			"Desc":       "id",       
			"MyFilter":   "",         
			"QueryCnt":   0,          
			"Query":      "",         
			"Asc":        "",         
			"Offset":     0,          
			"ReturnJson": "",         
		},
	}

	// 4. API 요청 실행 (GateToken 포함)
	Body, err := RequestWithGateToken(mainApiURL, gateToken, requestBody)
	if err != nil {
		// 요청 실패 시 에러 로그 출력
		e.ErrLog("API 요청 실패: ", err)
		return
	}

	// 5. 응답 데이터 출력
	fmt.Println("API 응답 데이터:", string(Body))
}

📌 저장된 GateToken으로 요청 (만료된 GateToken 재 발급)

// GateToken을 사용하여 API 요청을 보내는 함수
func RequestWithGateToken(mainApiURL string, gateToken string, requestBody map[string]interface{}) ([]byte, error) {
	// 1. HTTP 요청 보내기 (기존 GateToken 사용)
	resp, body, err := SendHttpRequest(mainApiURL, gateToken, requestBody)
	if err != nil {
		fmt.Println("HTTP 요청 실패:", err)
		return nil, err
	}
	defer resp.Body.Close()

	fmt.Println("응답 본문:", string(body)) // API 응답 출력

	// 2. 응답 코드가 505라면 -> GateToken 만료 (새로운 GateToken 발급 필요)
	if resp.StatusCode == 505 {
		fmt.Println("GateToken 만료! 새로운 GateToken 발급 시도..")

		// 기존 만료된 GateToken 초기화
		locals.GAppApis[0] = locals.AppApi{}

		// 스케줄러가 새로운 GateToken이 얻어올 때까지 대기 (최대 5초마다 확인)
		for locals.GAppApis[0].GateToken == "" {
			fmt.Println("새로운 GateToken 발급 중..")
			time.Sleep(5 * time.Second)
		}

		fmt.Println("새로운 GateToken 발급 완료!")

		// 새로운 GateToken으로 API 재요청
		newGateToken := locals.GAppApis[0].GateToken
		resp, body, err := SendHttpRequest(mainApiURL, newGateToken, requestBody)
		if err != nil {
			fmt.Println("HTTP 요청 실패:", err)
			return nil, err
		}
		defer resp.Body.Close()

		// 새로운 요청 응답 확인
		if resp.StatusCode == 200 {
			e.OkLog("새로운 GateToken을 사용한 HTTP 요청 성공!")
			return body, nil
		} else {
			return nil, fmt.Errorf("새로운 GateToken 요청 실패, 상태 코드: %d", resp.StatusCode)
		}
	}

	// 기존 GateToken으로 요청 성공한 경우
	e.OkLog("기존 GateToken을 사용한 HTTP 요청 성공!")
	return body, nil
}

// HTTP 요청을 보내는 함수
func SendHttpRequest(mainApiURL string, gateToken string, requestBody map[string]interface{}) (*http.Response, []byte, error) {
	// 1. JSON 데이터로 변환
	jsonData, err := json.Marshal(requestBody)
	if err != nil {
		fmt.Println("JSON 변환 실패:", err)
		return nil, nil, err
	}

	// 2. HTTP 요청 생성 (POST 요청)
	req, err := http.NewRequest("POST", mainApiURL, bytes.NewBuffer(jsonData))
	if err != nil {
		fmt.Println("HTTP 요청 생성 실패:", err)
		return nil, nil, err
	}

	// 3. HTTP 헤더 설정 (GateToken 포함)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("GateToken", gateToken)

	// 4. HTTP 요청 실행
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("HTTP 요청 실패:", err)
		return nil, nil, err
	}

	// 5. 응답 데이터 읽기
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("응답 본문 읽기 실패:", err)
		return nil, nil, err
	}

	// 6. 응답 로그 출력
	fmt.Println("응답 코드:", resp.StatusCode)
	fmt.Println("응답 본문:", string(body))

	return resp, body, nil
}