Sign In

🥦 그라운드 학생 정보 조회 API 가이드

👉🏻 그라운드에서 학생 정보를 조회하는 API 기능을 설명합니다.

목차

시작하기

그라운드 학생 정보 조회 API를 사용하면 외부 애플리케이션에서 학생의 상세 정보를 조회할 수 있습니다. 포인트, 레벨, 드래곤 정보, 보유 아이템 등을 단일 API 호출로 가져올 수 있습니다.

기본 정보

프로토콜: HTTPS
인증 방식: API Key (Header)
필요 권한: readStudents

조회 가능한 정보

✅ 포인트 및 레벨 정보
✅ 드래곤 정보 (이름, 단계, 레벨, 경험치, 먹이, 하트스톤)
✅ 보유 마켓 상품 (일반 마켓 + 공동구매)
✅ 텃밭 아이템 (배치 아이템, 인벤토리, 하트베리)

API 키 발급

1. 그라운드에 로그인

교사 계정으로 그라운드에 로그인합니다.

2. 내 정보 페이지 이동

우측 상단 프로필 → 내 정보 클릭

3. API 키 관리 탭

API 키 관리 탭을 선택합니다.

4. 새 API 키 생성

1.
"새 API 키 생성" 버튼 클릭
2.
다음 정보 입력:
API 키 이름: 애플리케이션 이름 (예: "대시보드 앱")
만료 기간: 선택 (무제한 또는 30일/90일/180일/1년)
권한 설정:
학생 정보 조회 권한 (readStudents) - 필수
호출 제한: 기본값(분당 60회, 일일 500회)
3.
"API 키 생성" 클릭

5. API 키 저장

⚠️ 중요: API 키는 한 번만 표시됩니다!
sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p4
반드시 안전한 곳에 복사하여 보관하세요.

제한 사항

교사 계정당 최대 3개의 API 키 생성 가능
일일 최대 500회 호출 제한

인증 방식

모든 API 요청에는 X-API-Key 헤더가 필요합니다.
X-API-Key: sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p4

학생 정보 조회 API

엔드포인트

GET /api/v1/classes/{classId}/students/{studentCode}

URL 파라미터

파라미터
타입
설명
예시
classId
string
클래스 ID (그라운드에서 확인)
NP0hetJ3wyQKFtRnFeftmPiy8Dl4_2
studentCode
number
학생 번호 (출석번호)
2
💡 classId 확인 방법: 그라운드 → 클래스 관리 → 클래스 카드에서 "ID" 확인

요청 헤더

헤더
필수
X-API-Key
발급받은 API 키
⚠️ 권한 필요: API 키에 readStudents 권한이 있어야 합니다.

요청 예시

curl -X GET \ "https://growndcard.com/api/v1/classes/NP0hetJ3wyQKFtRnFeftmPiy8Dl4_2/students/2" \ -H "X-API-Key: sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p4"

성공 응답 (200 OK)

{ "success": true, "data": { "studentId": "stu_abc123", "studentCode": 2, "studentName": "홍길동", "avatar": "/avatars/a1.png", "points": { "totalPoints": 150, "rewardPoints": 180, "penaltyPoints": 30, "currentLevel": 4, "levelName": "아스파라거스" }, "dragon": { "name": "불꽃이", "stage": "baby", "level": 3, "absoluteLevel": 8, "experience": 45, "food": 12, "color": "red", "heartstones": 5 }, "marketInventory": { "totalItems": 12, "regularMarket": { "totalCount": 5, "products": [ { "productName": "연필 세트", "productEmoji": "✏️" }, { "productName": "지우개", "productEmoji": "🧹" } ] }, "commonMarket": { "totalCount": 4, "products": [ { "productName": "간식 세트", "productEmoji": "🍪" } ] }, "cave": { "totalCount": 3, "products": [ { "productName": "드래곤 먹이", "productEmoji": "🥩" } ] } }, "farmItems": { "heartBerries": 25, "placedItems": [ { "itemId": "item_001", "name": "분수", "emoji": "⛲", "x": 2, "y": 3 }, { "itemId": "item_002", "name": "나무", "emoji": "🌳", "x": 4, "y": 2 } ], "inventoryItems": [ { "itemId": "item_003", "name": "꽃", "emoji": "🌸", "quantity": 5 }, { "itemId": "item_004", "name": "벤치", "emoji": "🪑", "quantity": 2 } ], "farmBackground": "spring", "placedItemBonusPoints": 2.5 } }, "message": "학생 정보 조회에 성공했습니다." }

응답 데이터 상세

학생 기본 정보

필드
타입
설명
예시
studentId
string
학생의 내부 ID
"stu_abc123"
studentCode
number
학생 번호 (출석번호)
2
studentName
string
학생 이름
"홍길동"
avatar
string
아바타 이미지 경로
"/avatars/a1.png"

포인트 정보 (points)

필드
타입
설명
범위
totalPoints
number
총 포인트 (리워드 - 페널티)
-
rewardPoints
number
누적 리워드 포인트
>= 0
penaltyPoints
number
누적 페널티 포인트
>= 0
currentLevel
number
현재 레벨
1-50
levelName
string
레벨 명칭
"아몬드" 등 (식물 이름)

드래곤 정보 (dragon)

필드
타입
설명
name
string
드래곤 이름 (베이비 단계부터 설정 가능)
"불꽃이"
stage
string
드래곤 단계
egg, baby, junior, strong, super
level
number
단계별 레벨
1-5
absoluteLevel
number
절대 레벨 (전체 진행도)
0-25
experience
number
현재 경험치
>= 0
food
number
보유 먹이 개수
>= 0
color
string
드래곤 색상
red, blue, green
heartstones
number
보유 하트스톤 개수
>= 0
💡 드래곤 정보가 없으면 null이 반환됩니다 (아직 초기화되지 않은 경우).

마켓 인벤토리 (marketInventory)

학생의 마켓 상품 정보입니다.
필드
타입
설명
totalItems
number
전체 구매 기록 개수 (3가지 마켓 타입의 총합)
regularMarket
object
일반 마켓 상품 정보
commonMarket
object
공동구매 상품 정보
cave
object
케이브 (드래곤 동굴) 상품 정보

각 마켓 타입 구조 (regularMarket, commonMarket, cave)

필드
타입
설명
totalCount
number
해당 마켓 타입의 상품 개수
products
array
보유 중인 상품 목록 (중복 제거됨)

상품 객체 (products 배열의 각 항목)

필드
타입
설명
productName
string
상품 이름
productEmoji
string
상품 이모지 (선택)
💡 totalItems vs totalCount
totalItems: 전체 보유 상품의 총 개수 (일반 마켓 + 공동구매 + 케이브 합계)
totalCount: 각 마켓 타입별 보유 상품 개수
예: 연필 3개, 지우개 2개 보유 → totalCount = 5개
예: 일반 마켓 5개 + 공동구매 4개 + 케이브 3개 → totalItems = 12개
📝 products 배열 특징
현재 보유 중인 상품의 종류만 표시 (같은 상품을 여러 개 보유해도 1개만 표시)
개별 상품의 보유 개수는 표시하지 않음
"어떤 종류의 상품을 보유하고 있는가"를 보여주는 용도
예: 연필 3개 보유 → products 배열에 "연필" 1개만 표시
⚠️ 중요: 실시간 보유 수량
totalCounttotalItems현재 실제 보유 중인 해당 카테고리 및 총 상품 개수입니다
실시간으로 학생의 현재 상품 보유 상태를 정확히 반영합니다

텃밭 아이템 (farmItems)

필드
타입
설명
heartBerries
number
보유 하트베리 개수
placedItems
array
텃밭에 배치된 아이템 목록
inventoryItems
array
인벤토리 아이템 목록 (수량 포함)
farmBackground
string
텃밭 배경
placedItemBonusPoints
number
배치 아이템의 포인트 보너스 합계
placedItems 구조
{ itemId: string; // 아이템 ID name: string; // 아이템 이름 emoji: string; // 아이템 이모지 x: number; // X 좌표 y: number; // Y 좌표 }
inventoryItems 구조
{ itemId: string; // 아이템 ID name: string; // 아이템 이름 emoji: string; // 아이템 이모지 quantity: number; // 보유 수량 }

에러 처리

에러 응답 형식

{ "success": false, "error": { "code": "error_code", "message": "에러 메시지", "details": {} } }

에러 코드

상태 코드
에러 코드
설명
해결 방법
400
invalid_student_code
학생 번호가 제공되지 않음
studentCode 확인
401
unauthorized
API 키가 없거나 유효하지 않음
API 키 확인, X-API-Key 헤더 확인
403
forbidden
해당 클래스 접근 권한 없음
classId 확인
403
insufficient_permissions
학생 조회 권한이 없음
API 키의 readStudents 권한 확인
404
student_not_found
학생을 찾을 수 없음
studentCode 확인
429
rate_limit_exceeded
호출 제한 초과
잠시 후 재시도
500
internal_error
서버 내부 오류
잠시 후 재시도, 반복되면 문의

에러 응답 예시

{ "success": false, "error": { "code": "insufficient_permissions", "message": "학생 정보 조회 권한이 없습니다.", "details": { "requiredPermission": "readStudents" } } }

코드 예제

cURL

curl -X GET \ "https://growndcard.com/api/v1/classes/NP0hetJ3wyQKFtRnFeftmPiy8Dl4_2/students/2" \ -H "X-API-Key: sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p4"

JavaScript (Node.js)

const fetch = require('node-fetch'); const API_KEY = 'sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p4'; const BASE_URL = 'https://growndcard.com'; const CLASS_ID = 'NP0hetJ3wyQKFtRnFeftmPiy8Dl4_2'; async function getStudentInfo(studentCode) { try { const response = await fetch( `${BASE_URL}/api/v1/classes/${CLASS_ID}/students/${studentCode}`, { method: 'GET', headers: { 'X-API-Key': API_KEY } } ); const data = await response.json(); if (!response.ok) { throw new Error(`API Error: ${data.error.message}`); } console.log('학생 정보 조회 성공:', data.data); return data.data; } catch (error) { console.error('학생 정보 조회 실패:', error.message); throw error; } } // 사용 예시 getStudentInfo(2) .then(student => { console.log(`학생: ${student.studentName}`); console.log(`총 포인트: ${student.points.totalPoints}`); console.log(`레벨: ${student.points.currentLevel} (${student.points.levelName})`); if (student.dragon) { console.log(`드래곤: ${student.dragon.name || '이름 없음'} (Lv.${student.dragon.absoluteLevel})`); } console.log(`보유 상품 수: ${student.marketProducts.length}개`); console.log(`하트베리: ${student.farmItems.heartBerries}개`); }) .catch(error => console.error('에러:', error));

Python

import requests import json API_KEY = 'sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p4' BASE_URL = 'https://growndcard.com' CLASS_ID = 'NP0hetJ3wyQKFtRnFeftmPiy8Dl4_2' def get_student_info(student_code): """학생 정보 조회""" url = f'{BASE_URL}/api/v1/classes/{CLASS_ID}/students/{student_code}' headers = { 'X-API-Key': API_KEY } try: response = requests.get(url, headers=headers) response.raise_for_status() data = response.json() print(f"학생 정보 조회 성공: {data['data']}") return data['data'] except requests.exceptions.HTTPError as e: error_data = response.json() print(f"API 에러: {error_data['error']['message']}") raise except Exception as e: print(f"요청 실패: {str(e)}") raise # 사용 예시 if __name__ == '__main__': student = get_student_info(2) print(f"학생: {student['studentName']}") print(f"총 포인트: {student['points']['totalPoints']}") print(f"레벨: {student['points']['currentLevel']} ({student['points']['levelName']})") if student['dragon']: dragon_name = student['dragon'].get('name', '이름 없음') print(f"드래곤: {dragon_name} (Lv.{student['dragon']['absoluteLevel']})") print(f"전체 마켓 상품: {student['marketInventory']['totalItems']}개") print(f"일반 마켓: {student['marketInventory']['regularMarket']['totalCount']}개") print(f"하트베리: {student['farmItems']['heartBerries']}개")

TypeScript (React/Next.js)

interface StudentInfoResponse { success: boolean; data?: { studentId: string; studentCode: number; studentName: string; avatar?: string; points: { totalPoints: number; rewardPoints: number; penaltyPoints: number; currentLevel: number; levelName: string; }; dragon: { name?: string; stage: string; level: number; absoluteLevel: number; experience: number; food: number; color?: string; heartstones: number; } | null; marketInventory: { totalItems: number; regularMarket: { totalCount: number; products: Array<{ productName: string; productEmoji?: string; }>; }; commonMarket: { totalCount: number; products: Array<{ productName: string; productEmoji?: string; }>; }; cave: { totalCount: number; products: Array<{ productName: string; productEmoji?: string; }>; }; }; farmItems: { heartBerries: number; placedItems: Array<{ itemId: string; name: string; emoji: string; x: number; y: number; }>; inventoryItems: Array<{ itemId: string; name: string; emoji: string; quantity?: number; }>; farmBackground?: string; placedItemBonusPoints?: number; }; }; message?: string; } class GroundStudentApiClient { private apiKey: string; private baseUrl: string; private classId: string; constructor(apiKey: string, classId: string) { this.apiKey = apiKey; this.classId = classId; this.baseUrl = 'https://growndcard.com'; } async getStudentInfo(studentCode: number): Promise<StudentInfoResponse> { const url = `${this.baseUrl}/api/v1/classes/${this.classId}/students/${studentCode}`; const response = await fetch(url, { method: 'GET', headers: { 'X-API-Key': this.apiKey } }); const data = await response.json(); if (!response.ok) { throw new Error(data.error?.message || 'API 호출 실패'); } return data; } } // 사용 예시 const client = new GroundStudentApiClient( 'sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6', 'NP0hetJ3wyQKFtRnFeftmPiy8Dl2_2' ); async function example() { try { const result = await client.getStudentInfo(2); const student = result.data; if (!student) return; console.log(`학생: ${student.studentName}`); console.log(`총 포인트: ${student.points.totalPoints}`); console.log(`레벨: ${student.points.currentLevel} (${student.points.levelName})`); if (student.dragon) { console.log(`드래곤: ${student.dragon.name || '이름 없음'} (Lv.${student.dragon.absoluteLevel})`); console.log(`먹이: ${student.dragon.food}개, 경험치: ${student.dragon.experience}`); } console.log(`전체 마켓 상품: ${student.marketInventory.totalItems}개`); console.log(`일반 마켓: ${student.marketInventory.regularMarket.totalCount}개`); console.log(`공동구매: ${student.marketInventory.commonMarket.totalCount}개`); console.log(`케이브: ${student.marketInventory.cave.totalCount}개`); console.log(`하트베리: ${student.farmItems.heartBerries}개`); console.log(`배치 아이템: ${student.farmItems.placedItems.length}개`); console.log(`인벤토리 아이템: ${student.farmItems.inventoryItems.length}종류`); } catch (error) { console.error('학생 정보 조회 실패:', error); } }

PHP

<?php function getStudentInfo($studentCode) { $apiKey = 'sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p4'; $classId = 'NP0hetJ3wyQKFtRnFeftmPiy8Dl4_2'; $baseUrl = 'https://growndcard.com'; $url = "{$baseUrl}/api/v1/classes/{$classId}/students/{$studentCode}"; $options = [ 'http' => [ 'method' => 'GET', 'header' => [ "X-API-Key: {$apiKey}" ] ] ]; $context = stream_context_create($options); $response = file_get_contents($url, false, $context); if ($response === false) { throw new Exception('API 호출 실패'); } $result = json_decode($response, true); if (!$result['success']) { throw new Exception($result['error']['message']); } return $result['data']; } // 사용 예시 try { $student = getStudentInfo(2); echo "학생: {$student['studentName']}\n"; echo "총 포인트: {$student['points']['totalPoints']}\n"; echo "레벨: {$student['points']['currentLevel']} ({$student['points']['levelName']})\n"; if ($student['dragon']) { $dragonName = $student['dragon']['name'] ?? '이름 없음'; echo "드래곤: {$dragonName} (Lv.{$student['dragon']['absoluteLevel']})\n"; } echo "전체 마켓 상품: {$student['marketInventory']['totalItems']}개\n"; echo "일반 마켓: {$student['marketInventory']['regularMarket']['totalCount']}개\n"; echo "하트베리: {$student['farmItems']['heartBerries']}개\n"; } catch (Exception $e) { echo "에러: " . $e->getMessage() . "\n"; }

제한 사항

API 키 제한

교사 계정당 최대 3개 API 키 생성 가능
API 키는 생성 시 한 번만 표시됨 (분실 시 재발급 필요)

호출 제한 (Rate Limit)

분당 최대: 60회
일일 최대: 500회
제한 초과 시 429 Too Many Requests 응답

데이터 제한

마켓 상품: 실제 구매한 상품만 조회 가능
텃밭 아이템: 현재 보유 중인 아이템만 조회 가능
드래곤: 초기화되지 않은 경우 null 반환

보안 권장사항

1.
⚠️ API 키를 절대 공개 저장소에 커밋하지 마세요
2.
✅ 환경 변수(.env)에 저장하세요
3.
✅ 프론트엔드에 노출하지 말고 백엔드에서만 사용하세요
4.
✅ 정기적으로 API 키를 재발급하세요
5.
✅ 학생 개인정보 보호에 유의하세요

FAQ

Q1. classId는 어디서 확인하나요?

A: 그라운드 → 클래스 관리 → 각 클래스 카드의 "ID" 필드에서 확인할 수 있습니다.

Q2. 학생 번호(studentCode)는 무엇인가요?

A: 학생의 출석번호입니다. 선생님이 등록하신 그라운드의 학생 목록에서 확인할 수 있습니다.

Q3. API 키를 분실했어요!

A: API 키는 재조회가 불가능합니다. 기존 키를 삭제하고 새로운 키를 발급받으세요.

Q4. 여러 학생의 정보를 한 번에 조회할 수 있나요?

A: 현재는 단일 학생만 조회 가능합니다. 여러 학생을 조회하려면 각 학생마다 API를 호출해야 합니다.

Q5. 드래곤 정보가 null로 나옵니다.

A: 학생이 아직 드래곤을 초기화하지 않은 경우 null이 반환됩니다. 학생이 그라운드에서 드래곤 시스템을 활성화하면 정보가 조회됩니다.

Q6. 조회 권한만 있으면 포인트 부여는 못하나요?

A: 네, readStudents 권한만으로는 포인트 부여가 불가능합니다. 포인트 부여가 필요하면 awardPoints 권한도 함께 활성화하세요.

Q7. 학생의 과거 포인트 이력도 조회할 수 있나요?

A: 현재는 누적 포인트와 현재 상태만 조회 가능합니다. 포인트 부여 이력은 그라운드 웹사이트에서만 확인할 수 있습니다.

활용 예시

대시보드 앱

학생들의 포인트와 레벨을 실시간으로 모니터링하는 대시보드를 만들 수 있습니다.

리더보드 시스템

여러 학생의 정보를 조회하여 순위를 매기는 리더보드를 구현할 수 있습니다.

분석 도구

학생들의 포인트 분포, 드래곤 레벨 통계 등을 분석하는 도구를 만들 수 있습니다.

학부모 앱

학생 코드로 자녀의 학습 진행 상황을 조회하는 앱을 만들 수 있습니다.

지원

문의

문의 게시판: 그라운드 → 문의 게시판