CraftPresence - DiscordSDK - private 멤버
CraftPresence - DiscordSDK - private 멤버
이번엔 Wrapper 안에 있는 함수들에 대해 설명해보겠습니다.
1. 전체 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//
// DiscordppWrapper.hpp
// CraftPresence
#ifndef DiscordppWrapper_hpp
#define DiscordppWrapper_hpp
#include "discordpp.h"
#include "cdiscord.h"
#include <memory>
#include <string>
#include <functional>
#include <optional>
// Swift에서 호출할 수 있는 C 스타일 함수 포인터 타입 정의
typedef void (*AuthorizeCallback)(void* context, bool success, const char* error);
typedef void (*LogoutCallback)(void* context, bool success, const char* error);
typedef void (*UserCallback)(void* context, bool success, const char* id, const char* username, const char* error);
typedef void (*ActivityCallback)(void* context, bool success, const char* error);
class DiscordppWrapper {
private:
std::shared_ptr<discordpp::Client> client;
std::string applicationId;
uint64_t numericApplicationId = 0;
std::optional<discordpp::AuthorizationCodeVerifier> currentCodeVerifier;
std::string currentCodeVerifierValue;
std::string lastRefreshToken;
public:
DiscordppWrapper(const std::string& appId);
~DiscordppWrapper();
bool isAuthorized() const;
bool isConnected() const;
void authorize(void* context, AuthorizeCallback callback);
void logout(void* context, LogoutCallback callback);
void getCurrentUser(void* context, UserCallback callback);
void updateActivity(const std::string& name,
const std::string& state,
const std::string& details,
const std::string& largeImageKey,
const std::string& smallImageKey,
int64_t startTimestamp,
int64_t endTimestamp,
int32_t activityType,
void* context,
ActivityCallback callback);
void clearActivity(void* context, ActivityCallback callback);
void runCallbacks();
private:
void exchangeCodeForToken(const std::string& code,
const std::string& redirectUri,
const std::string& codeVerifier,
void* context,
AuthorizeCallback callback);
void applyAccessToken(discordpp::AuthorizationTokenType tokenType,
const std::string& accessToken,
void* context,
AuthorizeCallback callback);
uint64_t resolvedApplicationId() const;
static std::string messageFromResult(const discordpp::ClientResult& result);
};
#endif /* DiscordppWrapper_hpp */
2. variable
1. client
1
std::shared_ptr<discordpp::Client> client;
- 역할: discordpp 라이브러리의 핵심 Client 인스턴스를 가리키는 스마트 포인터
- 생성/해제: 생성자는
std::make_shared<discordpp::Client>()로 초기화하고, 소멸자에서client.reset()으로 해제합니다. - 사용처: 인증, 토큰 갱신, 사용자 조회, Rich Presence 업데이트 등 모든
discordpp연산에 사용됩니다. - 주의:
shared_ptr라 참조 카운트로 소유권을 관리하지만, 멀티스레드 환경에서client자체의 스레드 안전성(discordpp구현)을 확인해야 한다.client가nullptr이면 대부분의public/private메서드는 조기 실패 처리(callback호출 또는false반환)를 합니다.
2. applicationId
1
std::string applicationId;
- 역할: 생성자에서 전달받는 애플리케이션 ID(보통 문자열 형태의 Discord App ID)를 그대로 저장합니다.
- 사용:
authorize()에서args.SetClientId(std::stoull(applicationId))로clientId설정 시에도 사용될 수 있고,numericApplicationId파싱 실패 시fallback으로 남아있습니다.
- 사용:
3. numericApplicationId
1
uint64_t numericApplicationId = 0;
- 역할:
applicationId를 숫자로 파싱한 값(캐시)입니다.- 초기화/설정: 생성자에서
applicationId가 비어있지 않으면stoull로 파싱 시도하고 성공하면numericApplicationId에 저장. 실패하면 0으로 유지. - 사용:
- 생성자 직후
numericApplicationId != 0이면client->SetApplicationId(numericApplicationId)를 호출합니다. resolvedApplicationId()에서numericApplicationId가 0이 아니면 이 값을 반환하고, 0이면client->GetApplicationId()를 시도합니다.
- 생성자 직후
- 주의: 파싱 예외는 try/catch로 잡아서
numericApplicationId를 0으로 설정합니다. 문자열이 숫자가 아닐 경우를 대비합니다.
- 초기화/설정: 생성자에서
4. currentCodeVerifier
1
std::optional<discordpp::AuthorizationCodeVerifier> currentCodeVerifier
- 역할: OAuth PKCE(Proof Key for Code Exchange) 흐름에서 사용되는
AuthorizationCodeVerifier객체를 보관합니다.- 생성/사용:
authorize()에서client->CreateAuthorizationCodeVerifier()를 호출해 생성하고currentCodeVerifier에 저장합니다.ApplyAccessToken(토큰 적용 성공) 시currentCodeVerifier.reset()으로 해제합니다.
- 주의:
optional로 되어 있어 생성 실패 시 현재 없음 상태를 표현합니다.- 내부에 PKCE 관련 challenge/verifier 문자열을 포함하므로 노출/로그에 주의해야 합니다(코드에는 일부 로그가 남음).
- 생성/사용:
5. currentCodeVerifierValue
1
std::string currentCodeVerifierValue
- 역할:
currentCodeVerifier의Verifier()값을 문자열로 저장한 것(직렬화된 PKCE verifier).- 사용:
authorize()안에서lambda를 위해 값(capturedVerifier)을 복사해 전달합니다. - 이유: lambda(비동기 콜백)는 함수가 리턴된 이후에 실행될 수 있으므로,
currentCodeVerifierValue를 캡처해 사용하거나 lambda 내부에 값을 명시적으로 캡처해야 수명 문제를 줄일 수 있습니다. - 정리:
applyAccessToken성공 시clear()로 지워집니다.
- 사용:
6. lastRefreshToken
1
std::string lastRefreshToken
- 역할:
GetToken호출 성공 시 응답에서 받은refresh token을 저장합니다.- 사용: 현재 코드에서는
refresh token을 단순 저장만 하고 이를 이용한 토큰 재발급 흐름 구현(예: 자동 리프레시)은 보이지 않습니다. - 주의:
refresh token은 민감 정보이므로 안전한 저장(예: 키체인, Keychain)·삭제가 필요합니다. 현재는 메모리(plain string)에만 보관됩니다.logout()구현이 비어있어lastRefreshToken을 지우지 않음 — 보안상 개선 권장.
- 사용: 현재 코드에서는
3. const, function
1. exchangeCodeForToken()
1
2
3
4
5
void exchangeCodeForToken(const std::string& code,
const std::string& redirectUri,
const std::string& codeVerifier,
void* context,
AuthorizeCallback callback);
- 목적: OAuth authorization code를 access token + refresh token으로 교환하는 단계(서버에 토큰 요청).
- 입력:
code:authorize()에서 받은 authorization code(사용자가 권한을 승인한 후 발급된 값).redirectUri:authorize()에서 사용된 redirect URI(Authorize 콜백이 제공).codeVerifier: PKCE 검증용 verifier(일반적으로authorize()에서 생성한 값의 복사본,authorize()에서는 capturedVerifier로 전달).context,callback: 외부 호출자(예: Swift)에게 결과를 알리기 위한 C 스타일 콜백 규약. (자세한 콜백 내용은 [[CraftPresence - DiscordSDK - CallBack Function#1.AuthorizeCallbackAuthorizeCallback]] 참조)
- 구현 세부(동작 흐름):
client존재 여부 확인; 없으면callback(context,false,"Client not initialized")호출.resolvedApplicationId()를 호출해 유효한 클라이언트 ID를 확보. 0이면 실패 처리.codeVerifier가 비어있으면 실패 처리(“Missing PKCE verifier”).client->GetToken(clientId, code, codeVerifier, redirectUri, lambda)호출. GetToken 비동기 콜백에서:- 실패:
messageFromResult(result)를 이용해 에러 메시지 생성 후callback(context,false,message.c_str())호출. - 성공:
lastRefreshToken = refreshToken저장,applyAccessToken(tokenType, accessToken, context, callback)호출.
- 실패:
- 주의/특징:
codeVerifier는 외부에서 복사된 문자열(capturedVerifier)을 넘겨주므로 원본currentCodeVerifier객체의 수명과 무관하게 사용 가능하도록 설계되어 있음.GetToken에서 받은refresh token을lastRefreshToken에 보관만 하고 추가 처리(예: 안전 저장, 자동 갱신)는 없음.- 에러 문자열 전달 시
message.c_str()를 바로 callback으로 전달하는데, 이 포인터의 수명(함수 종료 이후)을 보장하지 않으므로 호출자(예: Swift)가 즉시 복사해야 안전합니다.
2. applyAccessToken()
1
2
3
4
void applyAccessToken(discordpp::AuthorizationTokenType tokenType,
const std::string& accessToken,
void* context,
AuthorizeCallback callback);
- 목적: 획득한 access token을 discordpp client에 설정하고, 연결(connect)까지 수행한 뒤 인증 완료 콜백을 호출.
- 입력:
tokenType: 토큰의 종류(discordpp enum — OAuth 토큰/봇 토큰 등).accessToken: 실제 액세스 토큰 문자열.context, callback: 호출자 식별자 및 결과 콜백.
- 구현(동작 흐름):
client존재 여부 확인.client->UpdateToken(tokenType, accessToken, lambda)호출.UpdateToken콜백에서 실패 시messageFromResult로 에러 메시지 생성 후callback(context,false,message.c_str())호출.- 성공 시
client->Connect()호출(try/catch로 예외 처리). Connect 성공하면:currentCodeVerifier.reset()및currentCodeVerifierValue.clear()로 PKCE 관련 상태 정리.callback(context, true, nullptr)로 인증 성공 알림.
- 주의:
- UpdateToken과 Connect의 실패는 모두 catch/콜백으로 적절히 처리합니다.
- Connect가 예외를 던질 수 있으므로 try/catch로 잡고 callback으로 전달합니다.
- 접근 토큰을 client에 설정한 이후에야 Connect가 호출되므로 호출자 입장에서는 callback에서 “완전히 연결된” 상태로 보면 됩니다.
- callback에 전달되는 문자열이 임시 객체의 c_str()인 경우 수명 문제가 발생할 수 있습니다.(앞서의 권고와 동일).
3. resolvedApplicationId()
1
uint64_t resolvedApplicationId() const;
- 목적: 사용할 수 있는 numeric application id를 제공.
- 동작:
numericApplicationId != 0이면 그 값을 반환.- 그렇지 않으면 client가 있으면
client->GetApplicationId()호출(예외 시 0 반환). - 최종적으로 유효하지 않으면 0을 반환.
- 사용:
exchangeCodeForToken에서 클라이언트 ID를 결정할 때 사용된다. - 주의:
client->GetApplicationId()도 예외를 던질 수 있으므로 try/catch로 보호되어 있다.
4. messageFromResult
1
static std::string messageFromResult(const discordpp::ClientResult& result);
- 목적:
discordpp::ClientResult객체로부터 사람이 읽을 수 있는 오류/상태 메시지를 뽑아 std::string으로 반환. - 동작:
result.Error()를 먼저 가져와 message에 할당.- 비어있으면
result.ToString()시도. - 여전히 비어있으면 “Discord SDK error”로 대체.
- 완성된
std::string을 반환.
- 사용처:
GetToken/Authorize/UpdateToken등 실패 시 에러 메시지 생성에 사용된다. - 주의:
- 이 함수는
std::string을 반환하므로 호출 측에서message.c_str()를 바로 콜백에 넘기는 패턴은 함수 종료 후 해당c_str()가 가리키는 메모리의 유효성(호스트 언어의 즉시 복사 여부)에 의존합니다. 안전성 보장이 필요합니다.
- 이 함수는
이 포스트는 저작권자의 CC BY 4.0 라이센스를 따릅니다.