사용자 데이터 조회
엔드포인트
GET /userinfo설명
Access Token을 사용하여 인증된 사용자의 데이터를 조회합니다.
이 엔드포인트는 사용자의 기본 데이터와 연결된 대상(objectType)에 따라 학생 또는 선생님 상세 데이터를 반환합니다.
인증
이 엔드포인트는 Authorization 헤더에 Bearer Token을 포함해야 합니다.
Authorization: Bearer {accessToken}요청 파라미터
요청 파라미터는 없습니다. Access Token만 필요합니다.
응답
성공 응답 (200 OK)
{
"id": 1,
"email": "student@gsm.hs.kr",
"role": "USER",
"status": "ACTIVE",
"objectType": "STUDENT",
"isStudent": true,
"student": {
"id": 1,
"name": "홍길동",
"sex": "MAN",
"grade": 1,
"classNum": 1,
"number": 1,
"studentNumber": 1101,
"major": "SW_DEVELOPMENT",
"specialty": "백엔드",
"dormitoryFloor": 3,
"dormitoryRoom": 301,
"role": "GENERAL_STUDENT",
"isLeaveSchool": false
},
"teacher": null
}응답 필드
사용자 기본 데이터
| 필드 | 타입 | 설명 | 예시 |
|---|---|---|---|
id | Long | 사용자 고유 ID | 1 |
email | String | 사용자 이메일 주소 | student@gsm.hs.kr |
role | String | 사용자 역할 (USER, ADMIN) | USER |
status | String | 계정 상태 (PENDING, ACTIVE). 선생님 계정은 어드민 승인 전까지 PENDING으로 유지됩니다 | ACTIVE |
objectType | String? | 연결된 대상 종류 (STUDENT, TEACHER, nullable) | STUDENT |
isStudent | Boolean | (Deprecated) 학생 계정 여부. objectType == STUDENT의 파생값으로, 하위 호환을 위해 유지됩니다. 신규 연동은 objectType을 사용하세요 | true |
student | Object? | 학생 상세 데이터 (objectType이 STUDENT인 경우만 제공, nullable) | - |
teacher | Object? | 선생님 상세 데이터 (objectType이 TEACHER인 경우만 제공, nullable) | - |
학생 상세 데이터 (student 객체)
objectType이 STUDENT인 경우 다음 필드를 포함합니다.
| 필드 | 타입 | 설명 | 예시 |
|---|---|---|---|
id | Long | 학생 고유 ID | 1 |
name | String | 학생 이름 | 홍길동 |
sex | String | 성별 (MAN, WOMAN) | MAN |
grade | Int | 학년 (1-3) | 1 |
classNum | Int | 반 (1-4) | 1 |
number | Int | 번호 (1-18) | 1 |
studentNumber | Int | 학번 (4자리) | 1101 |
major | String | 학과 (SW_DEVELOPMENT, SMART_IOT, AI) | SW_DEVELOPMENT |
specialty | String? | 전공 (미설정 시 null) | 백엔드 |
dormitoryFloor | Int? | 기숙사 층 (nullable) | 3 |
dormitoryRoom | Int? | 기숙사 호실 (nullable) | 301 |
role | String | 학생 역할 (STUDENT_COUNCIL, DORMITORY_MANAGER, GENERAL_STUDENT, GRADUATE, WITHDRAWN) | GENERAL_STUDENT |
isLeaveSchool | Boolean | 자퇴 여부 | false |
선생님 상세 데이터 (teacher 객체)
objectType이 TEACHER인 경우 다음 필드를 포함합니다.
| 필드 | 타입 | 설명 | 예시 |
|---|---|---|---|
id | Long | 선생님 고유 ID | 1 |
name | String | 선생님 이름 | 김선생 |
email | String | 선생님 이메일 주소 | teacher@gsm.hs.kr |
department | String | 소속 부서 (MEISTER, DORMITORY, GRADE, ACADEMIC_AFFAIRS, PROFESSIONAL_EDUCATION, EMPLOYMENT_CAREER, ADMINISTRATION) | GRADE |
description | String? | 선생님 설명 (미설정 시 null) | 3학년 1반 담임선생님 |
오류 응답
| 상태 코드 | 설명 | 원인 |
|---|---|---|
401 Unauthorized | 인증 실패 | Access Token이 유효하지 않거나 만료됨 |
403 Forbidden | 권한 범위 부족 | 요청한 리소스에 대한 권한 범위 부족, 또는 선생님 계정이 어드민 승인 대기(PENDING) 상태 |
404 Not Found | 리소스를 찾을 수 없음 | 사용자 데이터를 찾을 수 없음 |
요청 예시
cURL
curl -X GET "https://oauth.resource.datagsm.kr/userinfo" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."응답 예시
학생 사용자
{
"id": 1,
"email": "student@gsm.hs.kr",
"role": "USER",
"status": "ACTIVE",
"objectType": "STUDENT",
"isStudent": true,
"student": {
"id": 1,
"name": "홍길동",
"sex": "MAN",
"grade": 1,
"classNum": 1,
"number": 1,
"studentNumber": 1101,
"major": "SW_DEVELOPMENT",
"specialty": "백엔드",
"dormitoryFloor": 3,
"dormitoryRoom": 301,
"role": "GENERAL_STUDENT",
"isLeaveSchool": false
},
"teacher": null
}선생님 사용자 (승인 완료)
{
"id": 2,
"email": "teacher@gsm.hs.kr",
"role": "USER",
"status": "ACTIVE",
"objectType": "TEACHER",
"isStudent": false,
"student": null,
"teacher": {
"id": 1,
"name": "김선생",
"email": "teacher@gsm.hs.kr",
"department": "GRADE",
"description": "3학년 1반 담임선생님"
}
}선생님 계정은 어드민 승인이 완료되어 status가 ACTIVE인 경우에만 로그인할 수 있습니다. status가 PENDING인 상태로 로그인을 시도하면 403 Forbidden 오류가 발생합니다.
실패 (401 Unauthorized)
{
"error": "unauthorized",
"error_description": "Access token expired or invalid"
}사용 예제
다음은 여러 언어에서 사용자 데이터를 조회하는 예제입니다.
async function getUserInfo(accessToken) {
try {
const response = await fetch('https://oauth.resource.datagsm.kr/userinfo', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error_description || 'Failed to get user info');
}
const userInfo = await response.json();
return userInfo;
} catch (error) {
console.error('Error:', error.message);
throw error;
}
}
// 사용 예시
const accessToken = sessionStorage.getItem('accessToken');
const userInfo = await getUserInfo(accessToken);
console.log('User ID:', userInfo.id);
console.log('Email:', userInfo.email);
// isStudent는 Deprecated이며 objectType == 'STUDENT'의 파생값입니다. 신규 연동은 objectType을 사용하세요.
if (userInfo.objectType === 'STUDENT') {
console.log('Student Name:', userInfo.student.name);
console.log('Grade:', userInfo.student.grade);
console.log('Class:', userInfo.student.classNum);
} else if (userInfo.objectType === 'TEACHER') {
console.log('Teacher Name:', userInfo.teacher.name);
console.log('Department:', userInfo.teacher.department);
}import requests
def get_user_info(access_token):
"""
Access Token으로 사용자 데이터를 조회합니다.
Args:
access_token: Access Token
Returns:
dict: 사용자 데이터
"""
try:
response = requests.get(
'https://oauth.resource.datagsm.kr/userinfo',
headers={'Authorization': f'Bearer {access_token}'}
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f'Error: {e}')
raise
# 사용 예시
access_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
user_info = get_user_info(access_token)
print(f'User ID: {user_info["id"]}')
print(f'Email: {user_info["email"]}')
# isStudent는 Deprecated이며 objectType == 'STUDENT'의 파생값입니다. 신규 연동은 objectType을 사용하세요.
if user_info['objectType'] == 'STUDENT':
student = user_info['student']
print(f'Student Name: {student["name"]}')
print(f'Grade: {student["grade"]}')
print(f'Class: {student["classNum"]}')
elif user_info['objectType'] == 'TEACHER':
teacher = user_info['teacher']
print(f'Teacher Name: {teacher["name"]}')
print(f'Department: {teacher["department"]}')import java.net.http.*;
import java.net.URI;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonProperty;
public class UserInfoService {
private static final HttpClient client = HttpClient.newHttpClient();
private static final ObjectMapper mapper = new ObjectMapper();
public static UserInfo getUserInfo(String accessToken) throws Exception {
// HTTP 요청 생성
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://oauth.resource.datagsm.kr/userinfo"))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
// 요청 전송
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
// 응답 처리
if (response.statusCode() != 200) {
throw new RuntimeException("Failed to get user info: " + response.body());
}
return mapper.readValue(response.body(), UserInfo.class);
}
public static class UserInfo {
public Long id;
public String email;
public String role;
public String status;
public String objectType;
// isStudent는 Deprecated이며 objectType == "STUDENT"의 파생값입니다. 신규 연동은 objectType을 사용하세요.
public Boolean isStudent;
public Student student;
public Teacher teacher;
}
public static class Student {
public Long id;
public String name;
public String sex;
public Integer grade;
public Integer classNum;
public Integer number;
public Integer studentNumber;
public String major;
public String specialty;
public Integer dormitoryFloor;
public Integer dormitoryRoom;
public String role;
public Boolean isLeaveSchool;
}
public static class Teacher {
public Long id;
public String name;
public String email;
public String department;
public String description;
}
public static void main(String[] args) throws Exception {
String accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
UserInfo userInfo = getUserInfo(accessToken);
System.out.println("User ID: " + userInfo.id);
System.out.println("Email: " + userInfo.email);
if ("STUDENT".equals(userInfo.objectType) && userInfo.student != null) {
System.out.println("Student Name: " + userInfo.student.name);
System.out.println("Grade: " + userInfo.student.grade);
} else if ("TEACHER".equals(userInfo.objectType) && userInfo.teacher != null) {
System.out.println("Teacher Name: " + userInfo.teacher.name);
System.out.println("Department: " + userInfo.teacher.department);
}
}
}import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.net.URI
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
data class UserInfo(
val id: Long,
val email: String,
val role: String,
val status: String,
val objectType: String?,
@Deprecated("objectType == \"STUDENT\" 으로 대체 예정, 하위 호환을 위해 임시 유지")
val isStudent: Boolean,
val student: Student?,
val teacher: Teacher?
)
data class Student(
val id: Long,
val name: String,
val sex: String,
val grade: Int,
val classNum: Int,
val number: Int,
val studentNumber: Int,
val major: String,
val specialty: String?,
val dormitoryFloor: Int?,
val dormitoryRoom: Int?,
val role: String,
val isLeaveSchool: Boolean
)
data class Teacher(
val id: Long,
val name: String,
val email: String,
val department: String,
val description: String?
)
class UserInfoService {
private val client = HttpClient.newHttpClient()
private val mapper = jacksonObjectMapper()
fun getUserInfo(accessToken: String): UserInfo {
val request = HttpRequest.newBuilder()
.uri(URI.create("https://oauth.resource.datagsm.kr/userinfo"))
.header("Authorization", "Bearer $accessToken")
.GET()
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() != 200) {
throw RuntimeException("Failed to get user info: ${response.body()}")
}
return mapper.readValue(response.body())
}
}
// 사용 예시
fun main() {
val service = UserInfoService()
val accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
val userInfo = service.getUserInfo(accessToken)
println("User ID: ${userInfo.id}")
println("Email: ${userInfo.email}")
// isStudent는 Deprecated이며 objectType == "STUDENT"의 파생값입니다. 신규 연동은 objectType을 사용하세요.
if (userInfo.objectType == "STUDENT" && userInfo.student != null) {
println("Student Name: ${userInfo.student.name}")
println("Grade: ${userInfo.student.grade}")
println("Class: ${userInfo.student.classNum}")
} else if (userInfo.objectType == "TEACHER" && userInfo.teacher != null) {
println("Teacher Name: ${userInfo.teacher.name}")
println("Department: ${userInfo.teacher.department}")
}
}학과 코드 (Major)
student.major 필드는 다음 값 중 하나입니다.
| 코드 | 설명 |
|---|---|
SW_DEVELOPMENT | 소프트웨어개발과 |
SMART_IOT | 스마트IoT과 |
AI | 인공지능과 |
학생 역할 코드 (Student Role)
student.role 필드는 다음 값 중 하나입니다.
| 코드 | 설명 |
|---|---|
GENERAL_STUDENT | 일반 학생 |
STUDENT_COUNCIL | 학생회 |
DORMITORY_MANAGER | 기숙사 사감 |
GRADUATE | 졸업생 |
WITHDRAWN | 자퇴생 |
선생님 부서 코드 (Department)
teacher.department 필드는 다음 값 중 하나입니다.
| 코드 | 설명 |
|---|---|
MEISTER | 마이스터부 |
DORMITORY | 사감선생님 |
GRADE | 학년부 |
ACADEMIC_AFFAIRS | 교무부 |
PROFESSIONAL_EDUCATION | 전문교육부 |
EMPLOYMENT_CAREER | 취업진로부 |
ADMINISTRATION | 행정실 |
계정 상태 코드 (Status)
status 필드는 다음 값 중 하나입니다.
| 코드 | 설명 |
|---|---|
PENDING | 어드민 승인 대기 중 (선생님 회원가입 직후). 이 상태에서는 로그인이 차단되어 Access Token을 발급받을 수 없습니다 |
ACTIVE | 활성화된 계정. 학생은 이메일 인증 후 즉시 ACTIVE가 되며, 선생님은 어드민 승인 후 ACTIVE가 됩니다 |
연결 대상 종류 (Object Type)
objectType 필드는 다음 값 중 하나입니다.
| 코드 | 설명 |
|---|---|
STUDENT | 학생 계정. student 필드에 상세 데이터가 포함됩니다 |
TEACHER | 선생님 계정. teacher 필드에 상세 데이터가 포함됩니다 |
보안 주의사항
Access Token 보호
- Access Token은 사용자의 개인정보에 접근할 수 있는 권한을 부여합니다.
- Access Token을 안전하게 저장하고, HTTPS를 통해서만 전송하세요.
- Access Token이 탈취되면 공격자가 사용자 데이터를 조회할 수 있습니다.
토큰 만료 처리
- Access Token은 1시간 후 만료됩니다.
- 만료 시
401 Unauthorized오류가 발생하며, Refresh Token으로 갱신해야 합니다.
async function getUserInfoWithRetry(accessToken, refreshToken) {
try {
return await getUserInfo(accessToken);
} catch (error) {
if (error.message.includes('expired')) {
// Access Token 갱신
const tokens = await refreshAccessToken(refreshToken);
// 새 토큰으로 재시도
return await getUserInfo(tokens.accessToken);
}
throw error;
}
}다음 단계
사용자 데이터를 성공적으로 조회했다면, 이제 애플리케이션에서 사용자를 식별하고 맞춤형 기능을 제공할 수 있습니다.
실전 구현 예제가 필요하면 Examples 기술 문서를 참고하세요.