1. API 목록

2. POST: 회원가입 이메일 인증

이메일 인증 시도는 계정당 하루에 5회만 가능합니다. 5회 이상 인증 코드 요청 시 24시간 뒤 재요청 가능합니다.
HTTP Request
POST /sign-up/email-auth HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 37
Host: localhost:8080

{
  "email" : "example@pusan.ac.kr"
}
HTTP Response
HTTP/1.1 201 Created
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 1. Request Fields
Path Type Description

email

String

가입 이메일

2.1. ⚠️ 실패 케이스

❌ Case 1: 인증 이메일이 부산대 이메일이 아님
POST /sign-up/email-auth HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 32
Host: localhost:8080

{
  "email" : "test@gmail.com"
}
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 66

{
  "message" : "부산대 이메일만 가입 가능합니다."
}
Path Type Description

email

String

잘못된 도메인의 이메일 (부산대 메일이 아님)

3. PATCH: 회원가입 이메일 인증코드 확인

HTTP Request
PATCH /sign-up/email-auth HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 67
Host: localhost:8080

{
  "email" : "example@pusan.ac.kr",
  "authCode" : "exampleCode"
}
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 2. Request Fields
Path Type Description

email

String

가입 이메일

authCode

String

인증 코드

3.1. ⚠️ 실패 케이스

❌ Case 1: 인증번호 불일치
PATCH /sign-up/email-auth HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 66
Host: localhost:8080

{
  "email" : "example@pusan.ac.kr",
  "authCode" : "wrong_code"
}
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 73

{
  "message" : "이메일 인증 코드가 일치하지 않습니다."
}
Path Type Description

email

String

가입 이메일

authCode

String

틀린 인증 코드

❌ Case 2: 만료된 인증코드
PATCH /sign-up/email-auth HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 68
Host: localhost:8080

{
  "email" : "example@pusan.ac.kr",
  "authCode" : "expired_code"
}
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 83

{
  "message" : "이메일 인증 코드 만료 시간이 초과되었습니다."
}
Path Type Description

email

String

가입 이메일

authCode

String

만료된 인증 코드

4. POST: 회원가입 (학생)

memberType`은 `STUDENT 또는 STAFF`로 전달합니다. 학생은 `STUDENT`로 가입하며 `ROLE_학생 권한이 부여됩니다.
HTTP Request
POST /sign-up HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 151
Host: localhost:8080

{
  "name" : "테스트회원",
  "studentId" : "202612345",
  "email" : "example@pusan.ac.kr",
  "password" : "qwer123!",
  "memberType" : "STUDENT"
}
HTTP Response
HTTP/1.1 201 Created
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 3. Request Fields
Path Type Description

name

String

회원 이름

studentId

String

회원의 학번

email

String

회원의 이메일

password

String

회원의 비밀번호

memberType

String

회원 유형 (STUDENT, STAFF)

5. POST: 회원가입 (교직원)

memberType`을 `STAFF`로 전달하면 교직원 정보(`StaffInfo)와 이메일·이름이 일치하는지 대조한 뒤, 일치하는 교직원 정보에 저장된 권한으로 가입됩니다.
HTTP Request
POST /sign-up HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 149
Host: localhost:8080

{
  "name" : "테스트회원",
  "studentId" : "202612345",
  "email" : "example@pusan.ac.kr",
  "password" : "qwer123!",
  "memberType" : "STAFF"
}
HTTP Response
HTTP/1.1 201 Created
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 4. Request Fields
Path Type Description

name

String

교직원 이름

studentId

String

교직원의 교번

email

String

교직원의 이메일

password

String

교직원의 비밀번호

memberType

String

회원 유형 (STUDENT, STAFF)

5.1. ⚠️ 실패 케이스

❌ Case 1: 일치하는 교직원 정보 없음
POST /sign-up HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 149
Host: localhost:8080

{
  "name" : "테스트회원",
  "studentId" : "202612345",
  "email" : "example@pusan.ac.kr",
  "password" : "qwer123!",
  "memberType" : "STAFF"
}
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 66

{
  "message" : "일치하는 교직원 정보가 없습니다."
}
Path Type Description

name

String

교직원 이름

studentId

String

교직원의 교번

email

String

교직원의 이메일

password

String

교직원의 비밀번호

memberType

String

회원 유형 (STUDENT, STAFF)

❌ Case 2: 회원 유형이 STUDENT, STAFF가 아님
POST /sign-up HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 151
Host: localhost:8080

{
  "name" : "테스트회원",
  "studentId" : "202612345",
  "email" : "example@pusan.ac.kr",
  "password" : "qwer123!",
  "memberType" : "TEACHER"
}
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 100

{
  "message" : "[memberType] TEACHER : 회원 유형은 STUDENT 또는 STAFF만 가능합니다."
}
Path Type Description

name

String

회원 이름

studentId

String

회원의 학번

email

String

회원의 이메일

password

String

회원의 비밀번호

memberType

String

잘못된 회원 유형 (STUDENT, STAFF 외의 값)

6. POST: 로그인

HTTP Request
POST /sign-in HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 64
Host: localhost:8080

{
  "email" : "example@pusan.ac.kr",
  "password" : "qwer123!"
}
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 109

{
  "memberId" : 1,
  "name" : "테스트회원",
  "token" : "exampleToken",
  "types" : [ "ROLE_학생" ]
}
Table 5. Request Fields
Path Type Description

email

String

로그인 이메일

password

String

비밀번호 (영문+숫자+특수문자 조합)

Table 6. Response Body’s Fields
Path Type Description

memberId

Number

회원 고유 식별자

name

String

회원 이름

token

String

JWT 액세스 토큰

types

Array

회원 권한 목록(회원, 관리자)

7. POST: 비밀번호 변경 이메일 인증

이메일 인증 시도는 계정당 하루에 5회만 가능합니다. 5회 이상 인증 코드 요청 시 24시간 뒤 재요청 가능합니다.
HTTP Request
POST /sign-in/password-reset/email-auth HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 37
Host: localhost:8080

{
  "email" : "example@pusan.ac.kr"
}
HTTP Response
HTTP/1.1 201 Created
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 7. Request Fields
Path Type Description

email

String

가입 이메일

8. PATCH: 비밀번호 변경 이메일 인증코드 확인

HTTP Request
PATCH /sign-in/password-reset/email-auth HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 67
Host: localhost:8080

{
  "email" : "example@pusan.ac.kr",
  "authCode" : "exampleCode"
}
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 8. Request Fields
Path Type Description

email

String

가입 이메일

authCode

String

인증 코드

8.1. ⚠️ 실패 케이스

회원가입 이메일 인증 실패 케이스와 동일

9. PATCH: 비밀번호 변경

HTTP Request
PATCH /sign-in/password-reset HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 72
Host: localhost:8080

{
  "email" : "example@pusan.ac.kr",
  "newPassword" : "newPassword1!"
}
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 9. Request Fields
Path Type Description

email

String

가입 이메일

newPassword

String

새로운 비밀번호

10. PATCH: 마이페이지에서 비밀번호 변경

HTTP Request
PATCH /members/me/password-reset HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 71
Host: localhost:8080

{
  "password" : "{noop}123456789",
  "newPassword" : "newPassword1!"
}
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 10. Request Fields
Path Type Description

password

String

기존 비밀번호

newPassword

String

새로운 비밀번호

10.1. ⚠️ 실패 케이스

❌ Case 1: 저장된 비밀번호와 입력한 기존 비밀번호 불일치
PATCH /members/me/password-reset HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 70
Host: localhost:8080

{
  "password" : "wrongPassword!",
  "newPassword" : "newPassword1!"
}
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 52

{
  "message" : "비밀번호가 틀렸습니다."
}
Path Type Description

password

String

기존 비밀번호와 일치하지 않는 비밀번호

newPassword

String

새로운 비밀번호

❌ Case 2: 변경하려는 비밀번호가 기존 비밀번호와 동일
PATCH /members/me/password-reset HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 73
Host: localhost:8080

{
  "password" : "{noop}123456789",
  "newPassword" : "{noop}123456789"
}
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 152

{
  "message" : "[newPassword] {noop}123456789 : 비밀번호는 8~16자여야 하며, 영문·숫자·특수문자를 모두 포함해야 합니다."
}
Path Type Description

password

String

기존 비밀번호

newPassword

String

기존 비밀번호와 동일한 비밀번호

11. GET : 가입 이메일 찾기

HTTP Request
GET /sign-in/1/email-find HTTP/1.1
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 37

{
  "email" : "example@pusan.ac.kr"
}
Table 11. /sign-in/{studentId}/email-find
Parameter Description

studentId

가입 학번

Table 12. Response Body’s Fields
Path Type Description

email

String

가입된 이메일

12. POST: 로컬 환경 OAuth 리다이렉트 설정

로컬 개발 환경에서 구글 소셜 로그인 테스트 시 사전에 호출해야 합니다. 호출하지 않으면 기본값인 배포 사이트 URL로 리다이렉트됩니다.
HTTP Request
POST /oauth2/set-redirect HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Set-Cookie: redirect_type=local; Path=/; Max-Age=180; Expires=Sun, 28 Jun 2026 15:24:33 GMT; Secure; HttpOnly; SameSite=None

13. GET: Google OAuth 로그인 리다이렉트

Spring Security가 자동으로 처리하는 엔드포인트입니다. CSRF 방어용 state 파라미터를 자동으로 생성하고 세션에 저장합니다.
HTTP Request
GET /oauth2/authorization/google HTTP/1.1
Host: opus.pnu.app
HTTP Response
HTTP/1.1 302 Found
Location: https://accounts.google.com/o/oauth2/v2/auth
          ?scope=email+profile
          &response_type=code
          &client_id=YOUR_CLIENT_ID
          &redirect_uri=https://opus.pnu.app/login/oauth2/code/google
          &state=SPRING_SECURITY_GENERATED_STATE

14. GET: Google OAuth 콜백

구글 인증 완료 후 구글 서버가 자동으로 호출하는 엔드포인트입니다. 직접 호출할 필요 없으며 에러 처리 부분만 참고해주세요.
HTTP Request
GET /login/oauth2/code/google?code=4%2F0AX4X...&state=GENERATED_STATE HTTP/1.1
Host: opus.pnu.app
HTTP Response (성공)
HTTP/1.1 302 Found
Location: https://opus.pnu.app/oauth/callback?token=eyJhbGci...
HTTP Response (실패)
HTTP/1.1 302 Found
Location: https://opus.pnu.app/oauth/callback?error=URL인코딩된값

14.1. ⚠️ 실패 케이스

Details
디코딩된 error 값 원인

access_denied

사용자가 구글 로그인 취소

authorization_request_not_found

세션 만료 또는 뒤로가기 후 재시도

invalid_state_parameter

CSRF 의심

server_error

구글 서버 오류

GENERAL_MEMBER_CANNOT_USE_SOCIAL_LOGIN

일반 회원이 소셜 로그인 시도

error 파라미터는 URL 인코딩되어 있으므로 반드시 decodeURIComponent 처리가 필요합니다!

15. GET: 메인 통계 요약

HTTP Request
GET /statistics/summary HTTP/1.1
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 95

{
  "totalMembers" : 100,
  "totalProjects" : 42,
  "totalLikes" : 128,
  "totalContests" : 5
}
Table 13. Response Body’s Fields
Path Type Description

totalMembers

Number

총 가입자 수

totalProjects

Number

등록된 프로젝트 수

totalLikes

Number

총 좋아요 수

totalContests

Number

진행된 대회 수

16. GET: 나의 프로젝트 조회

로그인한 사용자가 팀원으로 참여한 모든 대회의 프로젝트 목록을 조회합니다. 수상 정보를 포함하며, 페이지네이션 없이 전체 반환됩니다.
HTTP Request
GET /members/me/projects HTTP/1.1
Authorization: Bearer exampleToken
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 487

[ {
  "contestId" : 1,
  "contestName" : "제6회창의융합해커톤대회",
  "teamId" : 10,
  "teamName" : "PNUops",
  "projectName" : "SW성과관리시스템",
  "trackName" : "AI/빅데이터",
  "awards" : [ {
    "awardName" : "대상",
    "awardColor" : "#FF0000"
  } ]
}, {
  "contestId" : 2,
  "contestName" : "제5회창의융합해커톤대회",
  "teamId" : 22,
  "teamName" : "해커톤의신",
  "projectName" : "PNUDataKing",
  "trackName" : null,
  "awards" : [ ]
} ]
Table 14. Response Body’s Fields
Path Type Description

[].contestId

Number

대회 ID

[].contestName

String

대회명

[].teamId

Number

팀 ID

[].teamName

String

팀명

[].projectName

String

프로젝트명

[].trackName

String

트랙(분과)명

[].awards

Array

수상 내역 리스트

[].awards[].awardName

String

수상명

[].awards[].awardColor

String

수상 색상

17. GET: 나의 투표 기록 조회

현재 투표 기간에 해당하는 대회에서 투표한 프로젝트 목록을 조회합니다. 투표 기간이 아닌 경우 빈 배열을 반환하며, 최신순으로 정렬됩니다.
HTTP Request
GET /members/me/votes HTTP/1.1
Authorization: Bearer exampleToken
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 332

[ {
  "contestId" : 1,
  "contestName" : "제6회창의융합해커톤대회",
  "teamId" : 5,
  "teamName" : "Team Alpha",
  "projectName" : "알파 프로젝트"
}, {
  "contestId" : 1,
  "contestName" : "제6회창의융합해커톤대회",
  "teamId" : 12,
  "teamName" : "Team Beta",
  "projectName" : "베타 프로젝트"
} ]
Table 15. Response Body’s Fields
Path Type Description

[].contestId

Number

대회 ID

[].contestName

String

대회명

[].teamId

Number

팀 ID

[].teamName

String

팀명

[].projectName

String

프로젝트명

18. PATCH: 학번 수정

부산대 메일로 구글 소셜 로그인한 사용자에 한해 1회만 수정 가능합니다.
HTTP Request
PATCH /members/me/student-id HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer exampleToken
Content-Length: 31
Host: localhost:8080

{
  "studentId" : "202512345"
}
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 16. Request Fields
Path Type Description

studentId

String

변경할 학번

18.1. ⚠️ 실패 케이스

❌ Case 1: 학번 수정 불가 사용자 (소셜 회원 가입자만 1회의 학번 수정이 가능합니다.)
PATCH /members/me/student-id HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer exampleToken
Content-Length: 31
Host: localhost:8080

{
  "studentId" : "202512345"
}
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 88

{
  "message" : "소셜 회원 가입자만 1회의 학번 수정이 가능합니다."
}

19. 프로필 이미지 관리

19.1. GET: 프로필 이미지 조회

HTTP Request
GET /members/me/images/profile HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: image/png;charset=UTF-8
Accept-Ranges: bytes
Content-Length: 18

test-image-content
Table 17. Request Headers
Name Description

Authorization

Bearer (회원).access.token

19.1.1. ⚠️ 실패 케이스

❌ Case 1: 등록되지 않은 프로필 이미지
GET /members/me/images/profile HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP/1.1 404 Not Found
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 71

{
  "message" : "아이디와 일치하는 이미지가 없습니다"
}

19.2. PATCH: 프로필 이미지 변경

HTTP Request
PATCH /members/me/images/profile HTTP/1.1
Content-Type: multipart/form-data;charset=UTF-8; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm
Authorization: Bearer member.access.token
Host: localhost:8080

--6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm
Content-Disposition: form-data; name=image; filename=profile.png
Content-Type: image/png

test-image-content
--6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm--
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 18. Request Headers
Name Description

Authorization

Bearer (회원).access.token

Table 19. Request Parts
Part Description

image

변경할 프로필 이미지 (모든 이미지 형식 지원)

19.3. DELETE: 프로필 이미지 삭제

HTTP Request
DELETE /members/me/images/profile HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 20. Request Headers
Name Description

Authorization

Bearer (회원).access.token

20. DELETE: 회원 탈퇴

HTTP Request
DELETE /members/me HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 21. Request Headers
Name Description

Authorization

Bearer (회원).access.token

21. DELETE: 관리자 강제 회원 탈퇴

관리자 권한(ROLE_관리자)이 필요합니다.
HTTP Request
DELETE /admin/members/1 HTTP/1.1
Authorization: Bearer admin.access.token
Host: localhost:8080
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 22. Request Headers
Name Description

Authorization

Bearer (관리자).access.token

Table 23. /admin/members/{memberId}
Parameter Description

memberId

탈퇴시킬 회원 ID

21.1. ⚠️ 실패 케이스

❌ Case 1: 존재하지 않는 회원 ID
DELETE /admin/members/999 HTTP/1.1
Authorization: Bearer admin.access.token
Host: localhost:8080
HTTP/1.1 404 Not Found
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 54

{
  "message" : "회원을 찾을 수 없습니다."
}
Table 24. /admin/members/{memberId}
Parameter Description

memberId

존재하지 않는 회원 ID

22. GET: 회원 검색

관리자 권한(ROLE_관리자)이 필요합니다. 이메일 앞부분 일치로 검색하며(대소문자 무시) 최대 20건을 이메일 오름차순으로 반환합니다. `keyword`가 누락/빈값이면 빈 배열을 반환합니다. `roleType`을 지정하면 해당 역할의 회원만 필터링합니다.
HTTP Request
GET /admin/members/search?keyword=kim&roleType=ROLE_%ED%95%99%EC%83%9D HTTP/1.1
Authorization: Bearer admin.access.token
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 218

[ {
  "memberId" : 1,
  "name" : "김철수",
  "email" : "kim1@pusan.ac.kr",
  "roleType" : "ROLE_학생"
}, {
  "memberId" : 2,
  "name" : "김영희",
  "email" : "kim2@pusan.ac.kr",
  "roleType" : "ROLE_교수"
} ]
Table 25. Request Headers
Name Description

Authorization

Bearer (관리자).access.token

Table 26. Query Parameters
Parameter Description

keyword

검색할 이메일 (앞부분 일치, 대소문자 무시). 누락/빈값이면 빈 배열 반환

roleType

역할 필터 (ROLE_학생, ROLE_관리자, ROLE_교수, ROLE_직원, ROLE_외부멘토). 미지정 시 전체

Table 27. Response Body’s Fields
Path Type Description

[].memberId

Number

회원 ID

[].name

String

회원 이름

[].email

String

회원 이메일

[].roleType

String

회원 역할

22.1. ⚠️ 실패 케이스

❌ Case 1: 유효하지 않은 역할 값
GET /admin/members/search?keyword=kim&roleType=ROLE_%EC%97%86%EC%9D%8C HTTP/1.1
Authorization: Bearer admin.access.token
Host: localhost:8080
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 60

{
  "message" : "유효하지 않은 역할 값입니다."
}
Parameter Description

keyword

검색할 이메일

roleType

유효하지 않은 역할 값

23. GET: 계정 정보 조회

HTTP Request
GET /members/me HTTP/1.1
Authorization: Bearer exampleToken
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 117

{
  "name" : "테스트회원",
  "email" : "example@pusan.ac.kr",
  "githubUrl" : null,
  "isProfilePublic" : true
}
Table 28. Response Body’s Fields
Path Type Description

name

String

이름

email

String

이메일

githubUrl

String

GitHub 링크

isProfilePublic

Boolean

프로필 공개 여부

24. PATCH: GitHub 링크 수정

HTTP Request
PATCH /members/me/github-url HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer exampleToken
Content-Length: 51
Host: localhost:8080

{
  "githubUrl" : "https://github.com/hongjiyeon"
}
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 29. Request Fields
Path Type Description

githubUrl

String

GitHub URL

25. PATCH: 프로필 공개 여부 변경

HTTP Request
PATCH /members/me/profile-visibility HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer exampleToken
Content-Length: 30
Host: localhost:8080

{
  "isProfilePublic" : true
}
HTTP Response
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Table 30. Request Fields
Path Type Description

isProfilePublic

Boolean

프로필 공개 여부

26. GET: 나의 댓글 조회

사용자가 작성한 전체 댓글 목록을 필터링/정렬/페이지네이션과 함께 조회합니다.
HTTP Request
GET /members/me/comments?sort=latest&page=0&size=10 HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 1036

{
  "content" : [ {
    "comment" : {
      "commentId" : 1,
      "content" : "인정합니다.",
      "createdAt" : "2026-01-01T00:00:00",
      "memberName" : "홍지연"
    },
    "project" : {
      "contestId" : 1,
      "contestName" : "제6회창의융합해커톤대회",
      "categoryName" : "해커톤",
      "trackName" : "창업트랙",
      "teamId" : 5,
      "teamName" : "TeamName",
      "projectName" : "Project Name",
      "overview" : "Artify는 일상 속 모든 순간을 예술로 재해석하는 크리에이티브 플랫폼입니다."
    }
  } ],
  "pageable" : {
    "pageNumber" : 0,
    "pageSize" : 10,
    "sort" : {
      "empty" : false,
      "sorted" : true,
      "unsorted" : false
    },
    "offset" : 0,
    "unpaged" : false,
    "paged" : true
  },
  "last" : true,
  "totalElements" : 1,
  "totalPages" : 1,
  "size" : 10,
  "number" : 0,
  "sort" : {
    "empty" : false,
    "sorted" : true,
    "unsorted" : false
  },
  "first" : true,
  "numberOfElements" : 1,
  "empty" : false
}
Table 31. Query Parameters
Parameter Description

sort

정렬 기준 (latest, oldest)

startDate

조회 시작일 (yyyy-MM-dd)

endDate

조회 종료일 (yyyy-MM-dd)

page

페이지 번호 (0부터 시작)

size

페이지 크기

Table 32. Response Body’s Fields
Path Type Description

content[]

Array

댓글 목록

content[].comment.commentId

Number

댓글 ID

content[].comment.content

String

댓글 내용

content[].comment.createdAt

String

작성 일시 (ISO-8601)

content[].comment.memberName

String

작성자 이름

content[].project.contestId

Number

대회 ID

content[].project.contestName

String

대회명

content[].project.categoryName

String

카테고리명

content[].project.trackName

String

트랙명

content[].project.teamId

Number

팀 ID

content[].project.teamName

String

팀명

content[].project.projectName

String

프로젝트명

content[].project.overview

String

프로젝트 설명 (최대 100자)

pageable

Object

페이지 정보

last

Boolean

마지막 페이지 여부

totalPages

Number

전체 페이지 수

totalElements

Number

전체 요소 수

first

Boolean

첫 페이지 여부

size

Number

페이지 크기

number

Number

현재 페이지 번호

sort

Object

정렬 정보

numberOfElements

Number

현재 페이지 요소 수

empty

Boolean

비어있는 페이지 여부

26.1. ⚠️ 실패 케이스

❌ Case 1: 날짜 범위 불완전 (startDate와 endDate를 모두 입력해주세요)
GET /members/me/comments?startDate=2026-01-01 HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP/1.1 400 Bad Request
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 69

{
  "message" : "startDate와 endDate를 모두 입력해주세요"
}

27. GET: 나의 좋아요 미리보기

나의 활동 탭에서 보여줄 최근 좋아요한 프로젝트 미리보기를 조회합니다. createdAt 최신순으로 고정 3개를 반환합니다.
HTTP Request
GET /members/me/likes/preview HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 503

[ {
  "teamId" : 3,
  "teamName" : "Team Gamma",
  "contestId" : 1,
  "projectName" : "감마 프로젝트",
  "contestName" : "제6회창의융합해커톤대회"
}, {
  "teamId" : 7,
  "teamName" : "Team Delta",
  "contestId" : 2,
  "projectName" : "델타 프로젝트",
  "contestName" : "제5회창의융합해커톤대회"
}, {
  "teamId" : 15,
  "teamName" : "Team Epsilon",
  "contestId" : 2,
  "projectName" : "엡실론 프로젝트",
  "contestName" : "제5회창의융합해커톤대회"
} ]
Table 33. Response Body’s Fields
Path Type Description

[]

Array

좋아요 미리보기 목록 (최대 3개)

[].teamId

Number

팀 ID

[].teamName

String

팀명

[].contestId

Number

대회 ID

[].projectName

String

프로젝트명

[].contestName

String

대회명

28. GET: 나의 좋아요 전체 조회

사용자가 좋아요한 전체 프로젝트 목록을 필터링/정렬/페이지네이션과 함께 조회합니다.
HTTP Request
GET /members/me/likes?sort=latest&page=0&size=12 HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP Response
HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 829

{
  "content" : [ {
    "teamId" : 3,
    "teamName" : "Team Gamma",
    "projectName" : "감마 프로젝트",
    "contestId" : 1,
    "contestName" : "제6회창의융합해커톤대회"
  }, {
    "teamId" : 7,
    "teamName" : "Team Delta",
    "projectName" : "델타 프로젝트",
    "contestId" : 2,
    "contestName" : "제5회창의융합해커톤대회"
  } ],
  "pageable" : {
    "pageNumber" : 0,
    "pageSize" : 12,
    "sort" : {
      "empty" : false,
      "sorted" : true,
      "unsorted" : false
    },
    "offset" : 0,
    "unpaged" : false,
    "paged" : true
  },
  "last" : true,
  "totalElements" : 2,
  "totalPages" : 1,
  "size" : 12,
  "number" : 0,
  "sort" : {
    "empty" : false,
    "sorted" : true,
    "unsorted" : false
  },
  "first" : true,
  "numberOfElements" : 2,
  "empty" : false
}
Table 34. Query Parameters
Parameter Description

sort

정렬 기준 (latest, oldest)

startDate

조회 시작일 (yyyy-MM-dd)

endDate

조회 종료일 (yyyy-MM-dd)

categoryId

대회 카테고리 필터

contestId

특정 대회 필터

page

페이지 번호 (0부터 시작)

size

페이지 크기

Table 35. Response Body’s Fields
Path Type Description

content[]

Array

좋아요 프로젝트 목록

content[].teamId

Number

팀 ID

content[].teamName

String

팀명

content[].projectName

String

프로젝트명

content[].contestId

Number

대회 ID

content[].contestName

String

대회명

pageable

Object

페이지 정보

last

Boolean

마지막 페이지 여부

totalPages

Number

전체 페이지 수

totalElements

Number

전체 요소 수

first

Boolean

첫 페이지 여부

size

Number

페이지 크기

number

Number

현재 페이지 번호

sort

Object

정렬 정보

numberOfElements

Number

현재 페이지 요소 수

empty

Boolean

비어있는 페이지 여부