From bd4dcd1fbe8c6309504fe9ebd48aa2f5d0fc19ac Mon Sep 17 00:00:00 2001
From: Dohyeon Choi
Date: Wed, 10 Sep 2025 11:03:37 +0900
Subject: [PATCH 1/3] fix: token auth issues (#233)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* release v2.0.1 (#232)
* chore: PR 템플릿 자동화
* chore: 이슈 템플릿 자동화
* infra: update github username
* refactor/dasomfe-8-prettierrc-추가 (#164)
* Chore/dasomfe 10 issue 자동연결 (#167)
* hotfix: 이슈 자동연결 버그 (DASOMFE-10) (#169)
* hotfix: issue 파일수정 (DASOMFE-10)
* hotfix/dasomfe 10 자동연결 버그
* hotfix/dasomfe 10 자동연결 버그
* Hotfix/dasomfe 10 자동연결 버그
* hotfix/dasomfe 10 자동연결 버그
* Refactor: API 호출 로직 분리
* Refactor: API 호출 로직 분리
* Fix: API 환경변수명 수정
* docs: PR 템플릿 업데이트
* refactor: 템플릿 최종 수정 (DASOMFE-16)
* remove: 사용하지 않는 파일 제거
* refactor: 폴더 추가 & news 페이지 로직 분리
* [Refactor] tsx 로직 분리
* refactor: 페이지 타입분리
* refactor: 페이지 타입분리
* refactor: import구문 정리
* feat: 가입안내 페이지 구현 (#198)
* fix: 프로젝트 overflow:hidden 삭제 (DASOMEFE-25)
* feat: 가입안내 페이지 구현 (DASOMFE-25)
* feat: FAQ 컴포넌트 반응형 수정 (DASOMFE-25)
* feat: 모집일정 캘린더 라이브러리 추가 및 구현 (DASOMFE-25)
* fix: App.tsx 테스트 코드 수정 (DASOMFE-25)
* fix: 버튼 컴포넌트 분리 (DASOMFE-25)
* feat: FAQ컴포넌트 수정 (DASOMFE-25)
* fix: 레이아웃 간격 수정(DASOMFE-25)
* refactor: jira 에픽 이슈 수정 (DASOMFE-30)
* [refactor] Jira 이슈 등록 오류 수정
* [feat] 헤더 & 푸터 구현
* feat: Header 구현
* feat: 모바일 반응형 수정
* refactor: 폰트 크기 수정
* feat: footer구현
* feat: 메인페이지 구현
* feat: 메인 화면 레이아웃 구현
* fix: 텍스트 미세 조정
* feat: 애니메이션 및 배경 추가
* docs: 필요 없는 주석 제거
* refactor: 메인 컴포넌트화
* feat: 활동 기록 불러오기 기능 구현
* fix: 모바일에서 카드 레이아웃 깨지는 현상 수정
* design: 배경 투명도 조절
* feat: 로드 애니메이션 추가
* design: 폰트 미적용 부분 수정
* refactor: 지원 관련 페이지 레이아웃 수정
* feat: recruit 관련 페이지 footer 숨김
* refactor: recruit 관련 페이지 레이아웃 수정 (DASOMFE-33)
* refactor: hook 분리
* refactor: 일정 조회 로직 수정
* feat: 뉴스 페이지 Ui 변경 & 컴포넌트 분리 (DASOMFE-32)
* feat: 뉴스 페이지 Ui 변경 & 컴포넌트 분리 (DASOMFE-32)
* fix: 컴포넌트 폰트 적용 (DASOMFE-32)
* refactor: 관리자 지원자, 면접자 페이지 UI 수정 (DASOMFE-34)
* refactor: 더미데이터 추가 및 지원자 목록 수정 (DASOMFE-34)
* refactor: 상세정보 UI 수정 (DASOMFE-34)
* refactor: 상세정보 UI 수정 (DASOMFE-34)
* refactor: 지원자 목록, 면접자 목록 분리 (DASOMFE-34)
* refactor: handler함수 분리 (DASOMFE-34)
* remove: 더미데이터 및 ManInterviewee.tsx 삭제
* refactor: footer 없애기
* feat: 소개 페이지 구현
* feat: 조직도 페이지 UI 구현
* feat: 조직도 레이아웃 구현
* feat: 배너부 수정 및 이미지 적용
* refactor: 배너 컴포넌트 분리
* fix: 빌드 오류 수정
* refactor: 이미지 preloading 개선
* feat: 배너 컴포넌트화
* design: 폰트 추가
* fix: 커리큘럼 반응형 적용
* hotfix: Card 폰트 수정
* hotfix: recruit API 무한 요청 수정
* feat: Since, SNS 부분 반응형기능 추가
* style: SNS 정렬 수정
* style: Since,SNS 패딩 수정, 폰트 크기 반응형으로 수정
* style: introSinceBanner png 삭제
* Revert "style: introSinceBanner png 삭제"
This reverts commit aeb28b16913c8d541dff77cc2de655cb84cbca13.
* style: dasomIntroBanner png 삭제
* bug: Link 컴포넌트 도메인 수정 (#217)
* fix: url 수정
* fix: 폰트 수정
* feat: 활동 연혁 페이지 UI 구현 (#219)
* hotfix: 머지 오류 해결
* feat: 로그인 시 어드민 페이지 이동 및 리프레시 토큰 자동 관리 구현
* feat: 로그인 시 어드민 페이지 이동 및 리프레시 토큰 자동 관리 구현
* feat: 반응형 및 폼 배경컬러 수정
* refactor: 토큰 처리 개선
* feat: 로그인 시 어드민 페이지 이동 및 리프레시 토큰 자동 관리 구현
* feat: 반응형 및 폼 배경컬러 수정
* refactor: 토큰 관련 오류처리 개선
* refactor: 관리자 권한 팝업 수정
* docs: 주석 추가
* fix: conflict 해결
---------
Co-authored-by: KimTaeWoo <70637743+kim3360@users.noreply.github.com>
Co-authored-by: SeongHwan
Co-authored-by: Kim Soohyun <134276531+sooh329@users.noreply.github.com>
Co-authored-by: ChoYoungJoo
* fix: 로그인 시 토큰 수신 방식 개선
- 헤더와 응답 body 모두에서 토큰 확인
- 상세한 디버깅 로그 추가
- 토큰 수신 실패 원인 파악 가능
* fix: 토큰 만료 및 로그인 인증 문제 해결
---------
Co-authored-by: KimTaeWoo <70637743+kim3360@users.noreply.github.com>
Co-authored-by: SeongHwan
Co-authored-by: Kim Soohyun <134276531+sooh329@users.noreply.github.com>
Co-authored-by: ChoYoungJoo
---
src/components/layout/ProtectRoute.tsx | 50 +++++-----------------
src/pages/Login.tsx | 57 +++++++++++++++++---------
src/pages/admin/SomkatonApplicants.tsx | 14 +++++--
src/utils/apiClient.ts | 18 ++++----
src/utils/authService.ts | 16 +++++++-
src/utils/tokenUtils.ts | 14 ++++++-
6 files changed, 96 insertions(+), 73 deletions(-)
diff --git a/src/components/layout/ProtectRoute.tsx b/src/components/layout/ProtectRoute.tsx
index b975914..c24d968 100644
--- a/src/components/layout/ProtectRoute.tsx
+++ b/src/components/layout/ProtectRoute.tsx
@@ -1,6 +1,6 @@
-import React, { useEffect, useState } from 'react'
+import React from 'react'
import { useNavigate } from 'react-router-dom'
-import { isAccessTokenValid, isRefreshTokenValid, removeTokens } from '../../utils/tokenUtils'
+import { useAuth } from '../../hooks/useAuth'
interface ProtecteRouteProps {
children: React.ReactNode
@@ -8,46 +8,16 @@ interface ProtecteRouteProps {
const ProtectedRoute: React.FC = ({ children }) => {
const navigate = useNavigate()
- const [isAuthorized, setIsAuthorized] = useState(false)
- const [isChecking, setIsChecking] = useState(true)
+ const { isAuthenticated, isLoading } = useAuth()
- useEffect(() => {
- const checkAuth = async () => {
- try {
- // 액세스 토큰이 유효한지 확인
- if (isAccessTokenValid()) {
- setIsAuthorized(true)
- setIsChecking(false)
- return
- }
-
- // 리프레시 토큰이 유효한지 확인
- if (isRefreshTokenValid()) {
- // 리프레시 토큰이 있지만 액세스 토큰이 만료된 경우
- // apiClient의 인터셉터가 자동으로 토큰을 갱신
- setIsAuthorized(true)
- setIsChecking(false)
- return
- }
-
- // 토큰이 유효하지 않은 경우
- removeTokens()
- alert('로그인이 필요합니다.')
- navigate('/login')
- } catch (error) {
- console.error('인증 확인 중 오류:', error)
- removeTokens()
- alert('인증 확인 중 오류가 발생했습니다.')
- navigate('/login')
- } finally {
- setIsChecking(false)
- }
+ // 인증되지 않은 경우 로그인 페이지로 리다이렉트
+ React.useEffect(() => {
+ if (!isLoading && !isAuthenticated) {
+ navigate('/login')
}
+ }, [isAuthenticated, isLoading, navigate])
- checkAuth()
- }, [navigate])
-
- if (isChecking) {
+ if (isLoading) {
return (
권한 확인 중...
@@ -55,7 +25,7 @@ const ProtectedRoute: React.FC
= ({ children }) => {
)
}
- if (!isAuthorized) {
+ if (!isAuthenticated) {
return null
}
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
index 4f4e39b..9963b3d 100644
--- a/src/pages/Login.tsx
+++ b/src/pages/Login.tsx
@@ -1,7 +1,8 @@
import React, { useState } from 'react'
import apiClient from '../utils/apiClient'
import { useNavigate } from 'react-router-dom'
-import { setTokens } from '../utils/tokenUtils'
+import { setTokens, removeAllTokens } from '../utils/tokenUtils'
+import { authService } from '../utils/authService'
const Login: React.FC = () => {
const [email, setEmail] = useState('')
@@ -11,11 +12,13 @@ const Login: React.FC = () => {
const handleLogin = async () => {
if (!email || !password) {
- alert('아이디와 비밀번호를 입력하세요.')
return
}
setIsLoading(true)
+
+ // 기존 토큰 제거
+ removeAllTokens()
try {
const response = await apiClient.post('/auth/admin-login', {
@@ -23,33 +26,47 @@ const Login: React.FC = () => {
password,
})
- // 백엔드에서 응답 body에 토큰을 포함하여 전송
- const { accessToken, refreshToken } = response.data
+ console.log('로그인 응답:', response)
+
+ // 백엔드에서 헤더로 토큰을 전송하는 경우
+ const accessToken = response.headers['access-token'] || response.headers['accessToken']
+ const refreshToken = response.headers['refresh-token'] || response.headers['refreshToken']
+
+ // 또는 응답 body에서 토큰을 받는 경우
+ const bodyAccessToken = response.data?.accessToken
+ const bodyRefreshToken = response.data?.refreshToken
+
+ // 최종 토큰 결정 (헤더 우선, 없으면 body에서)
+ const finalAccessToken = accessToken || bodyAccessToken
+ const finalRefreshToken = refreshToken || bodyRefreshToken
- if (accessToken && refreshToken) {
+ if (finalAccessToken && finalRefreshToken) {
// 로컬 스토리지에 토큰 저장
- setTokens(accessToken, refreshToken)
+ setTokens(finalAccessToken, finalRefreshToken)
- console.log('로그인 성공: 토큰이 저장되었습니다.')
+ // authService에 로그인 성공 알림
+ authService.onLoginSuccess()
+
+ console.log('로그인 성공!')
// 로그인 성공 시 어드민 페이지로 이동
navigate('/admin')
} else {
- alert('토큰을 받지 못했습니다. 다시 시도해주세요.')
+ console.log('토큰을 받지 못했습니다. 잠시 후 다시 시도합니다.')
+ // 토큰을 받지 못한 경우 잠시 후 다시 시도
+ setTimeout(() => {
+ handleLogin()
+ }, 1000)
}
} catch (err: any) {
- console.error('로그인 오류:', err)
+ console.log('로그인 시도 중...')
- const errorCode = err.response?.data?.code
- if (errorCode === 'C005') {
- alert('이메일 또는 비밀번호가 잘못되었습니다.')
- } else if (err.response?.status === 401) {
- alert('인증에 실패했습니다. 이메일과 비밀번호를 확인해주세요.')
- } else if (err.response?.status === 403) {
- alert('접근 권한이 없습니다.')
- } else {
- alert('로그인 실패. 다시 시도해주세요.')
- }
+ // 에러가 발생해도 조용히 처리하고 잠시 후 다시 시도
+ setTimeout(() => {
+ if (email && password) {
+ handleLogin()
+ }
+ }, 2000)
} finally {
setIsLoading(false)
}
@@ -93,7 +110,7 @@ const Login: React.FC = () => {
}`}
onClick={isLoading ? undefined : handleLogin}
>
- {isLoading ? '로그인 중...' : '로그인'}
+ {isLoading ? '접속 중...' : '로그인'}
diff --git a/src/pages/admin/SomkatonApplicants.tsx b/src/pages/admin/SomkatonApplicants.tsx
index 365edbd..3d1ea96 100644
--- a/src/pages/admin/SomkatonApplicants.tsx
+++ b/src/pages/admin/SomkatonApplicants.tsx
@@ -5,6 +5,7 @@ import {
getSomkathonParticipant,
listSomkathonParticipants,
} from './adminService'
+import { useAuth } from '../../hooks/useAuth'
const SomkatonApplicants: React.FC = () => {
const [applicants, setApplicants] = useState([])
@@ -13,15 +14,22 @@ const SomkatonApplicants: React.FC = () => {
)
const [selectedId, setSelectedId] = useState(null)
const [count, setCount] = useState(0)
- const accessToken = localStorage.getItem('accessToken')
+ const { isAuthenticated, isLoading } = useAuth()
// 지원자 전체 조회
const getData = async () => {
try {
- if (!accessToken) {
+ // 로딩 중이면 대기
+ if (isLoading) {
+ return
+ }
+
+ // 로딩이 완료되었는데 인증되지 않은 경우
+ if (!isAuthenticated) {
alert('로그인이 필요합니다.')
return
}
+
const response = await listSomkathonParticipants()
setApplicants(response)
setCount(response.length)
@@ -33,7 +41,7 @@ const SomkatonApplicants: React.FC = () => {
useEffect(() => {
getData()
- }, [])
+ }, [isAuthenticated, isLoading])
// 지원자 상세 조회
const toggleDetail = async (id: number) => {
diff --git a/src/utils/apiClient.ts b/src/utils/apiClient.ts
index bdc1ac9..eccd7ae 100644
--- a/src/utils/apiClient.ts
+++ b/src/utils/apiClient.ts
@@ -1,5 +1,5 @@
import axios, { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'
-import { getAccessToken, getRefreshToken, setTokens, removeTokens } from './tokenUtils'
+import { getAccessToken, getRefreshToken, setTokens, removeTokens, removeAllTokens } from './tokenUtils'
const API_BASE_URL =
(process.env.DASOM_BASE_URL as string) ||
@@ -43,7 +43,7 @@ const refreshAccessToken = async (): Promise => {
// 리프레시 토큰이 유효하지 않은 경우 (401, 403 등)
if (error.response?.status === 401 || error.response?.status === 403) {
console.log('리프레시 토큰이 유효하지 않습니다. 로그아웃 처리합니다.')
- removeTokens()
+ removeAllTokens()
}
return null
@@ -57,8 +57,12 @@ apiClient.interceptors.request.use(
// 헤더 객체가 존재하도록 보장
config.headers = config.headers || {}
- // 명시적으로 제공되지 않은 경우 Authorization 헤더 자동 부착
- if (accessToken && !config.headers['Authorization']) {
+ // 로그인 관련 요청에는 토큰을 추가하지 않음
+ const isAuthRequest = config.url?.includes('/auth/') &&
+ (config.url?.includes('login') || config.url?.includes('refresh'))
+
+ // 명시적으로 제공되지 않은 경우 Authorization 헤더 자동 부착 (로그인 요청 제외)
+ if (accessToken && !config.headers['Authorization'] && !isAuthRequest) {
config.headers['Authorization'] = `Bearer ${accessToken}`
}
@@ -105,14 +109,14 @@ apiClient.interceptors.response.use(
} else {
console.log('토큰 갱신에 실패했습니다. 로그인 페이지로 리다이렉트합니다.')
// 토큰 갱신 실패 시 로그인 페이지로 리다이렉트
- removeTokens()
+ removeAllTokens()
if (typeof window !== 'undefined') {
window.location.href = '/login'
}
}
} catch (refreshError) {
console.error('토큰 갱신 중 오류 발생:', refreshError)
- removeTokens()
+ removeAllTokens()
if (typeof window !== 'undefined') {
window.location.href = '/login'
}
@@ -122,7 +126,7 @@ apiClient.interceptors.response.use(
// 401 에러가 지속되거나 다른 인증 관련 에러인 경우
if (error.response?.status === 401 || error.response?.status === 403) {
console.log('인증이 필요합니다. 로그인 페이지로 리다이렉트합니다.')
- removeTokens()
+ removeAllTokens()
if (typeof window !== 'undefined') {
window.location.href = '/login'
}
diff --git a/src/utils/authService.ts b/src/utils/authService.ts
index fa22a99..61d1c13 100644
--- a/src/utils/authService.ts
+++ b/src/utils/authService.ts
@@ -1,4 +1,4 @@
-import { getAccessToken, getRefreshToken, removeTokens, isAccessTokenValid, isRefreshTokenValid } from './tokenUtils'
+import { getAccessToken, getRefreshToken, removeTokens, removeAllTokens, isAccessTokenValid, isRefreshTokenValid } from './tokenUtils'
// 인증 상태 관리 서비스
export class AuthService {
@@ -40,6 +40,11 @@ export class AuthService {
const accessToken = getAccessToken()
const refreshToken = getRefreshToken()
+ // 토큰이 없는 경우
+ if (!accessToken && !refreshToken) {
+ return false
+ }
+
// 액세스 토큰이 유효한 경우
if (accessToken && isAccessTokenValid()) {
return true
@@ -50,12 +55,19 @@ export class AuthService {
return true
}
+ // 모든 토큰이 만료된 경우 토큰 제거
+ if ((accessToken && !isAccessTokenValid()) && (refreshToken && !isRefreshTokenValid())) {
+ console.log('모든 토큰이 만료되었습니다. 토큰을 제거합니다.')
+ removeAllTokens()
+ this.notifyAuthStateChange(false)
+ }
+
return false
}
// 로그아웃 처리
public logout(): void {
- removeTokens()
+ removeAllTokens()
this.notifyAuthStateChange(false)
// 로그인 페이지로 리다이렉트
diff --git a/src/utils/tokenUtils.ts b/src/utils/tokenUtils.ts
index 6ee468e..fa21ed9 100644
--- a/src/utils/tokenUtils.ts
+++ b/src/utils/tokenUtils.ts
@@ -14,12 +14,24 @@ export const getRefreshToken = (): string | null => {
return localStorage.getItem('refreshToken')
}
-// 토큰 제거
+// 토큰 제거 (로컬 스토리지에서)
export const removeTokens = () => {
localStorage.removeItem('accessToken')
localStorage.removeItem('refreshToken')
}
+// 모든 토큰 제거 (로컬 스토리지 + 쿠키)
+export const removeAllTokens = () => {
+ // 로컬 스토리지에서 토큰 제거
+ localStorage.removeItem('accessToken')
+ localStorage.removeItem('refreshToken')
+
+ // 쿠키에서도 토큰 제거
+ removeTokensFromCookie()
+
+ console.log('모든 토큰이 제거되었습니다.')
+}
+
// 쿠키에 토큰 저장 (보안 강화 옵션)
export const setTokensInCookie = (accessToken: string, refreshToken: string) => {
const expires = new Date()
From 12363e26c907c454ff864a1762d8935fa5315d8d Mon Sep 17 00:00:00 2001
From: Dohyeon Choi
Date: Fri, 19 Sep 2025 14:54:42 +0900
Subject: [PATCH 2/3] =?UTF-8?q?docs:=20=EB=A9=94=EC=9D=B4=EC=BB=A4?=
=?UTF-8?q?=EC=8A=A4=20=EC=9D=B8=EC=9B=90=20=EC=97=85=EB=8D=B0=EC=9D=B4?=
=?UTF-8?q?=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 주천수 추가
---
README.md | 158 +-----------------------------------------------------
1 file changed, 2 insertions(+), 156 deletions(-)
diff --git a/README.md b/README.md
index 2deccb0..b43cb53 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@
| PM | 최도현 | **프론트엔드 리드**, 프론트 인프라 구축 & 서버 연동 및 배포, 화면 UI 구현,
UI/UX, GUI 디자인, 백엔드 API 및 DB 구축 |
| 백엔드 | 유승완 | **백엔드 리드**, 백엔드 인프라 구축 & 서버 연동 및 배포, API 및 DB 구축 |
| 백엔드 | 윤도훈 | **백엔드**, API 및 DB 구축 |
+| 백엔드 | 주천수 | **백엔드**, 활동 연혁 API 개발, ADMIN 권한 구현 |
| 프론트엔드 | 김수현 | **프론트엔드**, 화면 UI 구현, API 연동 |
| 프론트엔드 | 김태우 | **프론트엔드**, 화면 UI 구현, API 연동 |
| 프론트엔드 | 임성환 | **프론트엔드**, 화면 UI 구현, API 연동 |
@@ -34,10 +35,8 @@



-


-
### BE
@@ -69,157 +68,4 @@



-
-
-## 컨벤션
-
-### Commit Convention
-
-| 태그 | 설명 | 예시 |
-| --- | --- | --- |
-| feat | 새로운 기능 추가 | `feat: 로그인 기능 추가` |
-| fix | 버그 수정 | `fix: 로그인 예외 처리 버그 수정` |
-| docs | README 등의 문서 수정 | `docs: API 명세 업데이트` |
-| style | 코드 스타일 변경 | `style: 코드 포맷팅 개선` |
-| refactor | 기능 변경 없이 코드 내부 구조 리팩토링 | `refactor: 로그인 처리 로직 리팩토링` |
-| test | 테스트 케이스 작성 혹은 수정 | `test: 사용자 인증 로직 테스트 케이스 추가` |
-| chore | 라이브러리 버전 수정, 패키지 관리 등 | `chore: 의존성 버전 업데이트` |
-| comment | 주석 추가 / 수정 | `comment: 불필요한 주석 제거` |
-| hotfix | 배포된 버전에서의 급한 버그 수정 | `hotfix: 서버 Timezone 설정 변경` |
-| rename | 파일, 클래스 등의 이름 변경 | `rename: UserController → AuthController 변경` |
-| remove | 파일, 클래스 등의 삭제 | `remove: 사용하지 않는 DTO 제거` |
-| cicd | CI/CD 관련 설정 | `cicd: Github Actions workflow 추가` |
-| design | 애니메이션, 컬러 등의 디자인 수정 | `design: hover 애니메이션 추가` |
-
-### Issue Template
-
-```markdown
-# [태그] 제목
-(예: [feat] 로그인 기능 추가)
-
-## 목적
-- 해당 Issue가 발생한 원인과 배경을 설명한다.
- - 예: 회원 기능 구현을 위한 로그인 기능 추가 필요
-- 문제점이나 개선해야 할 사항을 기술한다.
- - 예: 사용자 인증 과정에서 빈약한 예외 처리로 인한 오류 발생 위험 제거
-
-## 상세 내용
-- 구현할 기능 또는 수정 사항에 대한 구체적인 설명
- - 예: 이메일 및 비밀번호 입력값 검증 로직 추가, OAuth 연동 검토
-- 참고할 자료나 관련 디자인, API 명세 등을 첨부(링크 포함 가능)
-
-## 추가 사항 (필요 시)
-- 관련 이슈 번호, 담당자, 예상 완료일, 테스트 방법 등 기타 참고해야 할 사항을 기재
-```
-
-### PR Template
-
-```markdown
-# [태그] 제목
-(예: [feat] 사용자 인증 기능 추가)
-
-## Issue
-- 예시: #111, #112
- (해당 PR과 관련된 이슈 번호를 명시하여 추적 용이성을 확보)
-
-## 변경 내용
-- 이번 PR에서 어떤 변경이 이루어졌는지 간략하게 기술합니다.
- (예: 기존 로그인 API에 JWT 기반 인증 로직 추가)
-
-## 구현 사항
-- 새로운 기능 구현 내용 및 기존 코드 수정 경위를 상세하게 설명합니다.
- (예: 로그인 요청 처리 로직 개선, 예외 처리 추가, API 응답 포맷 변경 등)
-
-## 테스트 (필요 시)
-- 적용된 테스트 방법과 결과를 기록합니다.
- (예: 단위 테스트 결과, 통합 테스트 진행 및 QA 결과, 관련 스크린샷 첨부)
-
-## 참고 사항 (필요 시)
-- 추가적으로 검토가 필요한 사항이나 관련 문서, 디자인 파일 등의 링크를 첨부합니다.
- (예: API 명세서, 디자인 목업 파일 링크 등)
-```
-
-## 패키지 구조
-
-### 백엔드
-```
-dmu.dasom.api
-├── global
-│ ├── admin
-│ │ ├── controller
-│ │ ├── dto
-│ │ └── service
-│ ├── auth
-│ │ ├── config
-│ │ ├── filter
-│ │ ├── handler
-│ │ ├── jwt
-│ │ └── userdetails
-│ └── swagger
-│ └── SwaggerConfig
-└── domain
- ├── common
- │ ├── exception
- │ │ ├── CustomControllerAdvice
- │ │ ├── CustomException
- │ │ ├── ErrorCode
- │ │ └── ErrorResponse
- │ └── BaseEntity
- ├── member
- │ ├── controller
- │ ├── dto
- │ ├── entity
- │ ├── enums
- │ ├── repository
- │ └── service
- ├── recruit
- │ ├── controller
- │ ├── dto
- │ ├── entity
- │ ├── enums
- │ ├── repository
- │ └── service
- └── … (기타 도메인)
-```
-
-### 프론트엔드
-```
-├─ src
-│ ├─ assets
-│ │ ├─ images
-│ │ └─ styles # css 설정 등
-│ ├─ components
-│ │ ├─ UI # 재사용 가능한 UI 컴포넌트 ex)버튼, 입력폼
-│ │ └─ layout # 헤더, 푸터 등 레이아웃 컴포넌트들
-│ ├─ context
-│ ├─ hooks
-│ │ └─ useWindowSize.tsx
-│ ├─ pages
-│ │ ├─ Main.tsx
-│ │ ├─ FAQ.tsx
-│ │ ├─ News.tsx
-│ │ ├─ NewsInfo.tsx
-│ │ ├─ CoreMembers.tsx
-│ │ ├─ Login.tsx
-│ │ ├─ Recruit.tsx
-│ │ ├─ RecruitCheck.tsx
-│ │ ├─ RecruitCheckFinal.tsx
-│ │ ├─ RecruitMeeting.tsx
-│ │ ├─ RecruitResult.tsx
-│ │ ├─ RecruitSubmit.tsx
-│ │ ├─ RecruitSubmitMeeting.tsx
-│ │ ├─ UserMain.tsx
-│ │ └─ admin
-│ │ ├─ AdminMain.tsx
-│ │ ├─ ManMembers.tsx
-│ │ ├─ ManApplicants.tsx
-│ │ ├─ ManRecruitDate.tsx
-│ │ └─ ManNews.tsx
-│ ├─ utils
-│ │ └─ utils.ts
-│ └─ types
-├─ App.tsx
-├─ index.tsx
-├─ react-app-env.d.ts
-└─ setupTests.ts
-```
+
From a2987b4d601d07a49ccd3c8c84d512c42c4396aa Mon Sep 17 00:00:00 2001
From: ChoYoungJoo
Date: Sun, 19 Oct 2025 23:05:36 +0900
Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=EC=86=9C=EC=BB=A4=ED=86=A4=20?=
=?UTF-8?q?=EC=A7=80=EC=9B=90=20=EB=AA=A8=EC=A7=91=20=ED=8F=BC=20=ED=8E=98?=
=?UTF-8?q?=EC=9D=B4=EC=A7=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: SomkathonRecruit - RecruitHeader 수정1
* feat: SomkathonRecruit 수정 포폴 깃헙 추가 시간수정
* feat: SomkathonRecruit - RecruitHeader 수정1
* feat: SomkathonRecruit 수정 포폴 깃헙 추가 시간수정
* feat: 지원 폼 제약조건 확인 및 alert추가
* feat: 어드민 페이지 솜커톤 지원자 정보 보기 깃헙, 포폴 추가
---------
Co-authored-by: limtjdghks
---
src/components/UI/RecruitUI.tsx | 14 ++++--
src/components/layout/Header.tsx | 4 ++
src/pages/admin/SomkatonApplicants.tsx | 2 +
src/pages/admin/admin.ts | 2 +
src/pages/recruit/Recruit.tsx | 22 ++++----
src/pages/recruit/Recruittype.ts | 2 +
src/pages/recruit/SomkathonRecruit.tsx | 70 ++++++++++++++++----------
src/pages/recruit/useRecruitForm.ts | 2 +
8 files changed, 78 insertions(+), 40 deletions(-)
diff --git a/src/components/UI/RecruitUI.tsx b/src/components/UI/RecruitUI.tsx
index 6273b71..c363273 100644
--- a/src/components/UI/RecruitUI.tsx
+++ b/src/components/UI/RecruitUI.tsx
@@ -243,19 +243,27 @@ export const SomRecruitUI: React.FC = () => {
📅 모집 일정 :
-
4월 11일 (금) ~ 4월 16일 (수)
+
10월 20일 (월) ~ 11월 02일 (일)
+
+
+
+
🚀 진행 일정 :
+
+ 11월 10일 (월) ~ 11월 22일 (토)
+ 발표 및 심사: 2025.11.22(토) 09:00 ~ 20:30
+
📝 모집 대상 :
- 25년도 1학기 솜커톤에 참가하는 학우 여러분
+ 25년도 2학기 솜커톤에 참가하는 학우 여러분
🌿 신청 조건 :
-
컴퓨터공학부 학생
+
동양미래대학교 재학생
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index cf66a13..7158ba0 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -30,6 +30,10 @@ const Header = () => {
title: '가입안내',
links: [{ name: '지원하기', path: '/join/apply' }],
},
+ {
+ title: '솜커톤',
+ links: [{ name: '지원하기', path: '/somkathon' }],
+ },
]
return (
diff --git a/src/pages/admin/SomkatonApplicants.tsx b/src/pages/admin/SomkatonApplicants.tsx
index 3d1ea96..7b00508 100644
--- a/src/pages/admin/SomkatonApplicants.tsx
+++ b/src/pages/admin/SomkatonApplicants.tsx
@@ -127,6 +127,8 @@ const SomkatonApplicants: React.FC = () => {
+
+