1. API 목록
2. POST: 회원가입 이메일 인증
| 이메일 인증 시도는 계정당 하루에 5회만 가능합니다. 5회 이상 인증 코드 요청 시 24시간 뒤 재요청 가능합니다. |
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/1.1 201 Created
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
가입 이메일 |
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 |
|---|---|---|
|
|
잘못된 도메인의 이메일 (부산대 메일이 아님) |
3. PATCH: 회원가입 이메일 인증코드 확인
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/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
가입 이메일 |
|
|
인증 코드 |
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 |
|---|---|---|
|
|
가입 이메일 |
|
|
틀린 인증 코드 |
❌ 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 |
|---|---|---|
|
|
가입 이메일 |
|
|
만료된 인증 코드 |
4. POST: 회원가입
POST /sign-up HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 123
Host: localhost:8080
{
"name" : "테스트회원",
"studentId" : "202612345",
"email" : "example@pusan.ac.kr",
"password" : "qwer123!"
}
HTTP/1.1 201 Created
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
회원 이름 |
|
|
회원의 학번 |
|
|
회원의 이메일 |
|
|
회원의 비밀번호 |
5. POST: 로그인
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/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_회원" ]
}
| Path | Type | Description |
|---|---|---|
|
|
로그인 이메일 |
|
|
비밀번호 (영문+숫자+특수문자 조합) |
| Path | Type | Description |
|---|---|---|
|
|
회원 고유 식별자 |
|
|
회원 이름 |
|
|
JWT 액세스 토큰 |
|
|
회원 권한 목록(회원, 관리자) |
6. POST: 비밀번호 변경 이메일 인증
| 이메일 인증 시도는 계정당 하루에 5회만 가능합니다. 5회 이상 인증 코드 요청 시 24시간 뒤 재요청 가능합니다. |
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/1.1 201 Created
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
가입 이메일 |
7. PATCH: 비밀번호 변경 이메일 인증코드 확인
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/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
가입 이메일 |
|
|
인증 코드 |
7.1. ⚠️ 실패 케이스
| 회원가입 이메일 인증 실패 케이스와 동일 |
8. PATCH: 비밀번호 변경
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/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
가입 이메일 |
|
|
새로운 비밀번호 |
9. GET : 가입 이메일 찾기
GET /sign-in/1/email-find HTTP/1.1
Host: localhost:8080
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"
}
| Parameter | Description |
|---|---|
|
가입 학번 |
| Path | Type | Description |
|---|---|---|
|
|
가입된 이메일 |
10. POST: 로컬 환경 OAuth 리다이렉트 설정
| 로컬 개발 환경에서 구글 소셜 로그인 테스트 시 사전에 호출해야 합니다. 호출하지 않으면 기본값인 배포 사이트 URL로 리다이렉트됩니다. |
POST /oauth2/set-redirect HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
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=Sat, 28 Mar 2026 06:21:12 GMT; Secure; HttpOnly; SameSite=None
11. GET: Google OAuth 로그인 리다이렉트
Spring Security가 자동으로 처리하는 엔드포인트입니다. CSRF 방어용 state 파라미터를 자동으로 생성하고 세션에 저장합니다.
|
GET /oauth2/authorization/google HTTP/1.1 Host: opus.pnu.app
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
12. GET: Google OAuth 콜백
| 구글 인증 완료 후 구글 서버가 자동으로 호출하는 엔드포인트입니다. 직접 호출할 필요 없으며 에러 처리 부분만 참고해주세요. |
GET /login/oauth2/code/google?code=4%2F0AX4X...&state=GENERATED_STATE HTTP/1.1 Host: opus.pnu.app
HTTP/1.1 302 Found Location: https://opus.pnu.app/oauth/callback?token=eyJhbGci...
HTTP/1.1 302 Found Location: https://opus.pnu.app/oauth/callback?error=URL인코딩된값
12.1. ⚠️ 실패 케이스
Details
| 디코딩된 error 값 | 원인 |
|---|---|
|
사용자가 구글 로그인 취소 |
|
세션 만료 또는 뒤로가기 후 재시도 |
|
CSRF 의심 |
|
구글 서버 오류 |
|
일반 회원이 소셜 로그인 시도 |
error 파라미터는 URL 인코딩되어 있으므로 반드시 decodeURIComponent 처리가 필요합니다!
|
13. GET: 메인 통계 요약
GET /statistics/summary HTTP/1.1
Host: localhost:8080
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: 71
{
"totalProjects" : 42,
"totalLikes" : 128,
"totalContests" : 5
}
| Path | Type | Description |
|---|---|---|
|
|
등록된 프로젝트 수 |
|
|
총 좋아요 수 |
|
|
진행된 대회 수 |
14. GET: 나의 프로젝트 조회
| 로그인한 사용자가 팀원으로 참여한 모든 대회의 프로젝트 목록을 조회합니다. 수상 정보를 포함하며, 페이지네이션 없이 전체 반환됩니다. |
GET /members/me/projects HTTP/1.1
Authorization: Bearer exampleToken
Host: localhost:8080
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" : [ ]
} ]
| Path | Type | Description |
|---|---|---|
|
|
대회 ID |
|
|
대회명 |
|
|
팀 ID |
|
|
팀명 |
|
|
프로젝트명 |
|
|
트랙(분과)명 |
|
|
수상 내역 리스트 |
|
|
수상명 |
|
|
수상 색상 |
15. GET: 나의 투표 기록 조회
| 현재 투표 기간에 해당하는 대회에서 투표한 프로젝트 목록을 조회합니다. 투표 기간이 아닌 경우 빈 배열을 반환하며, 최신순으로 정렬됩니다. |
GET /members/me/votes HTTP/1.1
Authorization: Bearer exampleToken
Host: localhost:8080
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" : "베타 프로젝트"
} ]
| Path | Type | Description |
|---|---|---|
|
|
대회 ID |
|
|
대회명 |
|
|
팀 ID |
|
|
팀명 |
|
|
프로젝트명 |
16. PATCH: 학번 수정
| 부산대 메일로 구글 소셜 로그인한 사용자에 한해 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 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
변경할 학번 |
16.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회의 학번 수정이 가능합니다."
}
17. 프로필 이미지 관리
17.1. GET: 프로필 이미지 조회
GET /members/me/images/profile HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
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
| Name | Description |
|---|---|
|
Bearer (회원).access.token |
17.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" : "아이디와 일치하는 이미지가 없습니다"
}
17.2. PATCH: 프로필 이미지 변경
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/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Name | Description |
|---|---|
|
Bearer (회원).access.token |
| Part | Description |
|---|---|
|
변경할 프로필 이미지 (모든 이미지 형식 지원) |
17.3. DELETE: 프로필 이미지 삭제
DELETE /members/me/images/profile HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Name | Description |
|---|---|
|
Bearer (회원).access.token |
18. DELETE: 회원 탈퇴
DELETE /members/me HTTP/1.1
Authorization: Bearer member.access.token
Host: localhost:8080
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Name | Description |
|---|---|
|
Bearer (회원).access.token |
19. DELETE: 관리자 강제 회원 탈퇴
관리자 권한(ROLE_관리자)이 필요합니다.
|
DELETE /admin/members/1 HTTP/1.1
Authorization: Bearer admin.access.token
Host: localhost:8080
HTTP/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Name | Description |
|---|---|
|
Bearer (관리자).access.token |
| Parameter | Description |
|---|---|
|
탈퇴시킬 회원 ID |
19.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" : "회원을 찾을 수 없습니다."
}
| Parameter | Description |
|---|---|
|
존재하지 않는 회원 ID |
20. GET: 계정 정보 조회
GET /members/me HTTP/1.1
Authorization: Bearer exampleToken
Host: localhost:8080
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
}
| Path | Type | Description |
|---|---|---|
|
|
이름 |
|
|
이메일 |
|
|
GitHub 링크 |
|
|
프로필 공개 여부 |
21. PATCH: GitHub 링크 수정
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/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
GitHub URL |
22. PATCH: 프로필 공개 여부 변경
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/1.1 204 No Content
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
| Path | Type | Description |
|---|---|---|
|
|
프로필 공개 여부 |