From 0932ecd39e0cdfd45ecf97a8c703bdbea81a76a5 Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Fri, 20 Mar 2026 15:57:02 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=95=84=ED=82=A4=ED=85=8D=EC=B2=98=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EC=9D=B4=EC=8A=88=203=EA=B1=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 명령줄 토큰 노출 제거 — exec.Command에서 -token 인자 제거, 환경변수(A301_TOKEN)만 사용 - redeemTicket 재시도 추가 — 3회 exponential backoff, 4xx는 즉시 실패 - 임시 추출 디렉토리 defer os.RemoveAll 추가 — 중복 정리 코드 제거 Co-Authored-By: Claude Opus 4.6 (1M context) --- download.go | 4 +--- main.go | 4 ++-- protocol.go | 21 ++++++++++++++++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/download.go b/download.go index ccbb0d7..493016b 100644 --- a/download.go +++ b/download.go @@ -191,17 +191,15 @@ func doDownload(downloadURL, destDir string) error { if err != nil { return fmt.Errorf("임시 추출 디렉토리 생성 실패: %w", err) } + defer os.RemoveAll(tmpExtractDir) if err := extractZip(tmpPath, tmpExtractDir); err != nil { - os.RemoveAll(tmpExtractDir) return err } if err := moveContents(tmpExtractDir, destDir); err != nil { - os.RemoveAll(tmpExtractDir) return fmt.Errorf("파일 이동 실패: %w", err) } - os.RemoveAll(tmpExtractDir) return nil } diff --git a/main.go b/main.go index 8c2d0a7..90c054f 100644 --- a/main.go +++ b/main.go @@ -58,7 +58,7 @@ func handleURI(rawURI string) error { if _, statErr := os.Stat(gamePath); statErr == nil { ret := msgBox("One of the plans", "서버에 연결할 수 없습니다.\n설치된 게임을 실행하시겠습니까?\n(업데이트 확인 불가)", mbYesNo|mbQ) if ret == idYes { - cmd := exec.Command(gamePath, "-token", token) + cmd := exec.Command(gamePath) cmd.Dir = gameDir cmd.Env = append(os.Environ(), "A301_TOKEN="+token) if err := cmd.Start(); err != nil { @@ -86,7 +86,7 @@ func handleURI(rawURI string) error { return err } - cmd := exec.Command(gamePath, "-token", token) + cmd := exec.Command(gamePath) cmd.Dir = gameDir cmd.Env = append(os.Environ(), "A301_TOKEN="+token) if err := cmd.Start(); err != nil { diff --git a/protocol.go b/protocol.go index 5cd370b..83f4e9e 100644 --- a/protocol.go +++ b/protocol.go @@ -156,8 +156,24 @@ func fetchServerInfo() (*downloadInfo, error) { } // redeemTicket exchanges a one-time launch ticket for a fresh JWT access token. +// Retries up to 3 times with exponential backoff on transient errors. func redeemTicket(ticket string) (string, error) { - return redeemTicketFrom(redeemTicketURL, ticket) + const maxRetries = 3 + var lastErr error + for i := range maxRetries { + token, err := redeemTicketFrom(redeemTicketURL, ticket) + if err == nil { + return token, nil + } + lastErr = err + // HTTP 4xx errors should not be retried + var noRetry *errNoRetry + if errors.As(err, &noRetry) { + return "", err + } + time.Sleep(time.Duration(1<= 400 && resp.StatusCode < 500 { + return "", &errNoRetry{fmt.Errorf("런처 인증에 실패했습니다 (HTTP %d)", resp.StatusCode)} + } if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("런처 인증에 실패했습니다 (HTTP %d)", resp.StatusCode) }