Skip to content

Commit 22d023c

Browse files
committed
feat(analysis, global): 검증 로직 개선, 헤더 수정
1 parent 98c0588 commit 22d023c

File tree

3 files changed

+55
-26
lines changed

3 files changed

+55
-26
lines changed

backend/src/main/java/com/backend/domain/analysis/controller/AnalysisController.java

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.backend.global.response.ApiResponse;
1717
import jakarta.servlet.http.HttpServletRequest;
1818
import lombok.RequiredArgsConstructor;
19+
import lombok.extern.slf4j.Slf4j;
1920
import org.springframework.http.ResponseEntity;
2021
import org.springframework.transaction.annotation.Transactional;
2122
import org.springframework.web.bind.annotation.*;
@@ -24,6 +25,7 @@
2425
import java.util.ArrayList;
2526
import java.util.List;
2627

28+
@Slf4j
2729
@RestController
2830
@RequiredArgsConstructor
2931
@RequestMapping("/api/analysis")
@@ -84,16 +86,8 @@ public ResponseEntity<ApiResponse<HistoryResponseDto>> getAnalysisByRepositories
8486
Repositories repository = repositoryService.findById(repoId)
8587
.orElseThrow(() -> new BusinessException(ErrorCode.GITHUB_REPO_NOT_FOUND));
8688

87-
RepositoryResponse repositoryResponse = new RepositoryResponse(repository);
88-
8989
// 권한 검증
90-
Long jwtUserId = jwtUtil.getUserId(httpRequest);
91-
boolean isOwner = jwtUserId.equals(userId);
92-
boolean isPublic = repository.isPublicRepository();
93-
94-
if (!isOwner && !isPublic) {
95-
throw new BusinessException(ErrorCode.FORBIDDEN);
96-
}
90+
validateAccess(httpRequest, userId, repository);
9791

9892
// 2. 분석 결과 목록 조회 (최신순 정렬)
9993
List<AnalysisResult> analysisResults =
@@ -109,6 +103,7 @@ public ResponseEntity<ApiResponse<HistoryResponseDto>> getAnalysisByRepositories
109103
}
110104

111105
// 4. 응답 조합
106+
RepositoryResponse repositoryResponse = new RepositoryResponse(repository);
112107
HistoryResponseDto response = HistoryResponseDto.of(repositoryResponse, versions);
113108

114109
return ResponseEntity.ok(ApiResponse.success(response));
@@ -131,14 +126,7 @@ public ResponseEntity<ApiResponse<AnalysisResultResponseDto>> getAnalysisDetail(
131126
}
132127

133128
// 권한 검증
134-
Long jwtUserId = jwtUtil.getUserId(httpRequest);
135-
Repositories repository = analysisResult.getRepositories();
136-
boolean isOwner = jwtUserId.equals(userId);
137-
boolean isPublic = repository.isPublicRepository();
138-
139-
if (!isOwner && !isPublic) {
140-
throw new BusinessException(ErrorCode.FORBIDDEN);
141-
}
129+
validateAccess(httpRequest, userId, analysisResult.getRepositories());
142130

143131
AnalysisResultResponseDto response =
144132
new AnalysisResultResponseDto(analysisResult, analysisResult.getScore());
@@ -203,4 +191,41 @@ public SseEmitter stream(@PathVariable Long userId,
203191

204192
return analysisProgressService.connect(userId);
205193
}
194+
195+
/**
196+
* 리포지토리 접근 권한 검증
197+
* - 공개 리포지토리: 누구나 접근 가능 (비로그인 포함)
198+
* - 비공개 리포지토리: 소유자만 접근 가능
199+
*/
200+
201+
private void validateAccess(HttpServletRequest requeset, Long pathUserId, Repositories repository) {
202+
Long jwtUserId = jwtUtil.getUserId(requeset);
203+
204+
// 1. 공개 리포지토리일 경우
205+
if(repository.isPublicRepository()) {
206+
if (jwtUserId == null) {
207+
log.warn("비로그인 사용자의 공개 리포지토리 접근 시도: repoId={}", repository.getId());
208+
}
209+
210+
if (!jwtUserId.equals(pathUserId)) {
211+
log.warn("다른 사용자의 공개 리포지토리 접근 시도: jwtUserId={}, pathUserId={}, repoId={}",
212+
jwtUserId, pathUserId, repository.getId());
213+
}
214+
return;
215+
}
216+
217+
// 2. 비공개 리포지토리일 경우
218+
// 2-1. 비로그인
219+
if (jwtUserId == null) {
220+
log.warn("비로그인 사용자의 비공개 리포지토리 접근 시도: repoId={}", repository.getId());
221+
throw new BusinessException(ErrorCode.FORBIDDEN);
222+
}
223+
224+
// 2-2. 다른 사용자
225+
if (!jwtUserId.equals(pathUserId)) {
226+
log.warn("권한 없는 접근 시도: jwtUserId={}, pathUserId={}, repoId={}",
227+
jwtUserId, pathUserId, repository.getId());
228+
throw new BusinessException(ErrorCode.FORBIDDEN);
229+
}
230+
}
206231
}

front/src/components/Header.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,16 @@ export default function Header() {
4747
<div className="flex items-center gap-6">
4848
{isLoggedIn ? (
4949
<>
50-
<button onClick={guardNav("/history", "히스토리")} className="text-sm text-muted-foreground hover:text-foreground transition-colors">
51-
히스토리
52-
</button>
5350
<button onClick={handleCommunityClick} className="text-sm text-muted-foreground hover:text-foreground transition-colors">
5451
커뮤니티
5552
</button>
56-
<Button size="sm" className="bg-primary text-primary-foreground hover:bg-primary/90" onClick={() => router.push("/analysis")}>
57-
시작하기
58-
</Button>
53+
<button onClick={guardNav("/history", "히스토리")} className="text-sm text-muted-foreground hover:text-foreground transition-colors">
54+
분석내역
55+
</button>
56+
<button onClick={() => router.push("/analysis")} className="text-sm text-muted-foreground hover:text-foreground transition-colors">
57+
분석하기
58+
</button>
59+
<div className="w-px h-5 bg-border mx-2" />
5960
<button onClick={guardNav("/profile", "마이페이지")} className="text-sm text-muted-foreground hover:text-foreground transition-colors">
6061
{isLoadingUser ? '로딩중...' : user?.name ? `${user.name}(마이페이지)` : '마이페이지'}
6162
</button>
@@ -78,8 +79,8 @@ export default function Header() {
7879
<Button variant="ghost" size="sm" onClick={() => router.push("/login")}>
7980
로그인
8081
</Button>
81-
<Button size="sm" className="bg-primary text-primary-foreground hover:bg-primary/90" onClick={() => router.push("/analysis")}>
82-
시작하기
82+
<Button size="sm" className="bg-primary text-primary-foreground hover:bg-primary/90" onClick={() => router.push("/signup")}>
83+
회원가입
8384
</Button>
8485
</div>
8586
</>

front/src/hooks/auth/useAuth.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ export function useAuth() {
1616

1717
const fetchUserInfo = async () => {
1818
const token = localStorage.getItem('accessToken');
19-
if (!token) return;
19+
if (!token) {
20+
setIsInitializing(false);
21+
return;
22+
}
2023

2124
try {
2225
setIsLoadingUser(true);

0 commit comments

Comments
 (0)