Skip to content

Commit cd43e9a

Browse files
authored
refactor/OPS-319: 아카이브 로그인 연동 (#76)
* refactor/OPS-255 : datasource 테이블 sources 칼럼 추가 * refactor/OPS-319 : 아카이브 로그인 연동 * refactor/OPS-319 : 아카이브 로그인 연동
1 parent 79c2b3c commit cd43e9a

File tree

12 files changed

+627
-508
lines changed

12 files changed

+627
-508
lines changed

src/main/java/org/tuna/zoopzoop/backend/domain/archive/folder/controller/FolderController.java

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
import jakarta.validation.Valid;
44
import lombok.RequiredArgsConstructor;
55
import org.springframework.http.ResponseEntity;
6+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
67
import org.springframework.web.bind.annotation.*;
78
import org.tuna.zoopzoop.backend.domain.archive.folder.dto.FolderResponse;
89
import org.tuna.zoopzoop.backend.domain.archive.folder.dto.reqBodyForCreateFolder;
910
import org.tuna.zoopzoop.backend.domain.archive.folder.dto.resBodyForCreateFolder;
1011
import org.tuna.zoopzoop.backend.domain.archive.folder.service.FolderService;
1112
import org.tuna.zoopzoop.backend.domain.datasource.dto.FolderFilesDto;
13+
import org.tuna.zoopzoop.backend.domain.member.entity.Member;
1214
import org.tuna.zoopzoop.backend.global.rsData.RsData;
13-
import org.tuna.zoopzoop.backend.global.security.StubAuthUtil;
15+
import org.tuna.zoopzoop.backend.global.security.jwt.CustomUserDetails;
1416

1517
import java.util.HashMap;
1618
import java.util.List;
@@ -28,13 +30,13 @@ public class FolderController {
2830
* @param rq reqBodyForCreateFolder
2931
* @return resBodyForCreateFolder
3032
*/
31-
@PostMapping("")
33+
@PostMapping
3234
public RsData<resBodyForCreateFolder> createFolder(
33-
@Valid @RequestBody reqBodyForCreateFolder rq
35+
@Valid @RequestBody reqBodyForCreateFolder rq,
36+
@AuthenticationPrincipal CustomUserDetails userDetails
3437
) {
35-
// 임시 인증 정보
36-
Integer currentMemberId = StubAuthUtil.currentMemberId();
37-
FolderResponse createFile = folderService.createFolderForPersonal(currentMemberId, rq.folderName());
38+
Member member = userDetails.getMember();
39+
FolderResponse createFile = folderService.createFolderForPersonal(member.getId(), rq.folderName());
3840

3941
resBodyForCreateFolder rs = new resBodyForCreateFolder(createFile.folderName(), createFile.folderId());
4042

@@ -43,16 +45,19 @@ public RsData<resBodyForCreateFolder> createFolder(
4345
rq.folderName() + " 폴더가 생성됐습니다.",
4446
rs
4547
);
46-
4748
}
4849

4950
/**
5051
* 내 PersonalArchive 안의 folder 삭제
5152
* @param folderId 삭제할 folderId
5253
*/
5354
@DeleteMapping("/{folderId}")
54-
public ResponseEntity<Map<String, Object>> deleteFolder(@PathVariable Integer folderId) {
55-
String deletedFolderName = folderService.deleteFolder(folderId);
55+
public ResponseEntity<Map<String, Object>> deleteFolder(
56+
@PathVariable Integer folderId,
57+
@AuthenticationPrincipal CustomUserDetails userDetails
58+
) {
59+
Member member = userDetails.getMember();
60+
String deletedFolderName = folderService.deleteFolder(member.getId(), folderId);
5661

5762
Map<String, Object> body = new HashMap<>();
5863
body.put("status", 200);
@@ -70,10 +75,12 @@ public ResponseEntity<Map<String, Object>> deleteFolder(@PathVariable Integer fo
7075
@PatchMapping("/{folderId}")
7176
public ResponseEntity<Map<String, Object>> updateFolderName(
7277
@PathVariable Integer folderId,
73-
@RequestBody Map<String, String> body
78+
@RequestBody Map<String, String> body,
79+
@AuthenticationPrincipal CustomUserDetails userDetails
7480
) {
81+
Member member = userDetails.getMember();
7582
String newName = body.get("folderName");
76-
String updatedName = folderService.updateFolderName(folderId, newName);
83+
String updatedName = folderService.updateFolderName(member.getId(), folderId, newName);
7784

7885
Map<String, Object> response = new HashMap<>();
7986
response.put("status", 200);
@@ -87,13 +94,12 @@ public ResponseEntity<Map<String, Object>> updateFolderName(
8794
* 개인 아카이브의 폴더 이름 전부 조회
8895
* "default", "폴더1", "폴더2"
8996
*/
90-
@GetMapping("")
91-
public ResponseEntity<?> getFolders() {
92-
// 로그인된 멤버 ID 가져오기
93-
Integer currentMemberId = StubAuthUtil.currentMemberId();
94-
95-
// 내 personal archive 안의 폴더 조회
96-
List<FolderResponse> folders = folderService.getFoldersForPersonal(currentMemberId);
97+
@GetMapping
98+
public ResponseEntity<?> getFolders(
99+
@AuthenticationPrincipal CustomUserDetails userDetails
100+
) {
101+
Member member = userDetails.getMember();
102+
List<FolderResponse> folders = folderService.getFoldersForPersonal(member.getId());
97103

98104
return ResponseEntity.ok(
99105
Map.of(
@@ -108,10 +114,12 @@ public ResponseEntity<?> getFolders() {
108114
* 폴더(내 PersonalArchive 소속) 안의 파일 목록 조회
109115
*/
110116
@GetMapping("/{folderId}/files")
111-
public ResponseEntity<?> getFilesInFolder(@PathVariable Integer folderId) {
112-
Integer currentMemberId = StubAuthUtil.currentMemberId();
113-
114-
FolderFilesDto rs = folderService.getFilesInFolderForPersonal(currentMemberId, folderId);
117+
public ResponseEntity<?> getFilesInFolder(
118+
@PathVariable Integer folderId,
119+
@AuthenticationPrincipal CustomUserDetails userDetails
120+
) {
121+
Member member = userDetails.getMember();
122+
FolderFilesDto rs = folderService.getFilesInFolderForPersonal(member.getId(), folderId);
115123

116124
return ResponseEntity.ok(
117125
Map.of(

src/main/java/org/tuna/zoopzoop/backend/domain/archive/folder/repository/FolderRepository.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.springframework.data.jpa.repository.JpaRepository;
44
import org.springframework.data.jpa.repository.Query;
5+
import org.springframework.data.repository.query.Param;
56
import org.tuna.zoopzoop.backend.domain.archive.archive.entity.Archive;
67
import org.tuna.zoopzoop.backend.domain.archive.folder.entity.Folder;
78

@@ -45,4 +46,16 @@ public interface FolderRepository extends JpaRepository<Folder, Integer>{
4546
and f.isDefault = true
4647
""")
4748
Optional<Folder> findDefaultFolderByMemberId(Integer memberId);
49+
50+
// 한 번의 조인으로 존재 + 소유권(memberId) 검증
51+
@Query("""
52+
select f
53+
from Folder f
54+
join f.archive a
55+
join PersonalArchive pa on pa.archive = a
56+
where f.id = :folderId
57+
and pa.member.id = :memberId
58+
""")
59+
Optional<Folder> findByIdAndMemberId(@Param("folderId") Integer folderId,
60+
@Param("memberId") Integer memberId);
4861
}

src/main/java/org/tuna/zoopzoop/backend/domain/archive/folder/service/FolderService.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ public class FolderService {
3838
*/
3939
@Transactional
4040
public FolderResponse createFolderForPersonal(Integer currentMemberId, String folderName) {
41-
if (folderName == null || folderName.trim().isEmpty()) {
41+
if (folderName == null || folderName.trim().isEmpty())
4242
throw new IllegalArgumentException("폴더 이름은 비어 있을 수 없습니다.");
43-
}
4443

4544
Member member = memberRepository.findById(currentMemberId)
4645
.orElseThrow(() -> new IllegalArgumentException("멤버를 찾을 수 없습니다."));
@@ -115,8 +114,9 @@ private static String pickNextAvailable(String file, List<String> existing) {
115114
* soft delete 아직 구현 X
116115
*/
117116
@Transactional
118-
public String deleteFolder(Integer folderId) {
119-
Folder folder = folderRepository.findById(folderId)
117+
public String deleteFolder(Integer currentId, Integer folderId) {
118+
// 공격자에게 리소스 존재 여부를 노출 X (존재하지 않음 / 남의 폴더)
119+
Folder folder = folderRepository.findByIdAndMemberId(folderId, currentId)
120120
.orElseThrow(() -> new NoResultException("존재하지 않는 폴더입니다."));
121121

122122
if (folder.isDefault())
@@ -131,8 +131,8 @@ public String deleteFolder(Integer folderId) {
131131
* folderId에 해당하는 이름 변경
132132
*/
133133
@Transactional
134-
public String updateFolderName(Integer folderId, String newName) {
135-
Folder folder = folderRepository.findById(folderId)
134+
public String updateFolderName(Integer currentId, Integer folderId, String newName) {
135+
Folder folder = folderRepository.findByIdAndMemberId(folderId, currentId)
136136
.orElseThrow(() -> new NoResultException("존재하지 않는 폴더입니다."));
137137

138138
// 같은 아카이브 내에서 중복 폴더 이름 확인
@@ -173,18 +173,19 @@ public List<FolderResponse> getFoldersForPersonal(Integer memberId) {
173173
*/
174174
@Transactional(readOnly = true)
175175
public FolderFilesDto getFilesInFolderForPersonal(Integer memberId, Integer folderId) {
176-
Folder folder = folderRepository.findById(folderId)
176+
Folder folder = folderRepository.findByIdAndMemberId(folderId, memberId)
177177
.orElseThrow(() -> new NoResultException("존재하지 않는 폴더입니다."));
178178

179179
var files = dataSourceRepository.findAllByFolder(folder).stream()
180180
.map(ds -> new FileSummary(
181181
ds.getId(),
182182
ds.getTitle(),
183-
ds.getCreateDate(), // LocalDateTime
183+
ds.getDataCreatedDate(), // LocalDate
184184
ds.getSummary(),
185185
ds.getSourceUrl(),
186186
ds.getImageUrl(),
187-
ds.getTags() == null ? List.of() : ds.getTags()
187+
ds.getTags() == null ? List.of() : ds.getTags(),
188+
ds.getCategory() == null ? null : ds.getCategory().toString()
188189
))
189190
.toList();
190191

src/main/java/org/tuna/zoopzoop/backend/domain/datasource/controller/DatasourceController.java

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import jakarta.validation.Valid;
44
import lombok.RequiredArgsConstructor;
55
import org.springframework.http.ResponseEntity;
6+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
67
import org.springframework.web.bind.annotation.*;
78
import org.tuna.zoopzoop.backend.domain.datasource.dto.*;
89
import org.tuna.zoopzoop.backend.domain.datasource.service.DataSourceService;
9-
import org.tuna.zoopzoop.backend.global.security.StubAuthUtil;
10+
import org.tuna.zoopzoop.backend.domain.member.entity.Member;
11+
import org.tuna.zoopzoop.backend.global.security.jwt.CustomUserDetails;
1012

1113
import java.util.HashMap;
1214
import java.util.Map;
@@ -24,10 +26,14 @@ public class DatasourceController {
2426
* folderId 등록될 폴더 위치(null 이면 default)
2527
*/
2628
@PostMapping("")
27-
public ResponseEntity<?> createDataSource(@Valid @RequestBody reqBodyForCreateDataSource rq) {
29+
public ResponseEntity<?> createDataSource(
30+
@Valid @RequestBody reqBodyForCreateDataSource rq,
31+
@AuthenticationPrincipal CustomUserDetails userDetails
32+
) {
33+
// 로그인된 멤버 Id 사용
34+
Member member = userDetails.getMember();
35+
Integer currentMemberId = member.getId();
2836

29-
//임시 인증 정보
30-
Integer currentMemberId = StubAuthUtil.currentMemberId();
3137
int rs = dataSourceService.createDataSource(currentMemberId, rq.sourceUrl(), rq.folderId());
3238
return ResponseEntity.ok()
3339
.body(
@@ -39,8 +45,12 @@ public ResponseEntity<?> createDataSource(@Valid @RequestBody reqBodyForCreateDa
3945
* 자료 단건 삭제
4046
*/
4147
@DeleteMapping("/{dataSourceId}")
42-
public ResponseEntity<Map<String, Object>> delete(@PathVariable Integer dataSourceId) {
43-
int deletedId = dataSourceService.deleteById(dataSourceId);
48+
public ResponseEntity<Map<String, Object>> delete(
49+
@PathVariable Integer dataSourceId,
50+
@AuthenticationPrincipal CustomUserDetails userDetails
51+
) {
52+
Member member = userDetails.getMember();
53+
int deletedId = dataSourceService.deleteById(member.getId(), dataSourceId);
4454
return ResponseEntity.ok(
4555
Map.of(
4656
"status", 200,
@@ -55,11 +65,12 @@ public ResponseEntity<Map<String, Object>> delete(@PathVariable Integer dataSour
5565
*/
5666
@PostMapping("/delete")
5767
public ResponseEntity<Map<String, Object>> deleteMany(
58-
@Valid @RequestBody reqBodyForDeleteMany body
68+
@Valid @RequestBody reqBodyForDeleteMany body,
69+
@AuthenticationPrincipal CustomUserDetails userDetails
5970
) {
60-
dataSourceService.deleteMany(body.dataSourceId());
71+
Member member = userDetails.getMember();
72+
dataSourceService.deleteMany(member.getId(), body.dataSourceId());
6173

62-
// Map.of 는 null 불가 → LinkedHashMap 사용
6374
Map<String, Object> res = new java.util.LinkedHashMap<>();
6475
res.put("status", 200);
6576
res.put("msg", "복수개의 자료가 삭제됐습니다.");
@@ -75,9 +86,11 @@ public ResponseEntity<Map<String, Object>> deleteMany(
7586
@PatchMapping("/{dataSourceId}/move")
7687
public ResponseEntity<?> moveDataSource(
7788
@PathVariable Integer dataSourceId,
78-
@Valid @RequestBody reqBodyForMoveDataSource rq
89+
@Valid @RequestBody reqBodyForMoveDataSource rq,
90+
@AuthenticationPrincipal CustomUserDetails userDetails
7991
) {
80-
Integer currentMemberId = StubAuthUtil.currentMemberId();
92+
Member member = userDetails.getMember();
93+
Integer currentMemberId = member.getId();
8194

8295
DataSourceService.MoveResult result =
8396
dataSourceService.moveDataSource(currentMemberId, dataSourceId, rq.folderId());
@@ -101,8 +114,12 @@ public ResponseEntity<?> moveDataSource(
101114
* 자료 다건 이동
102115
*/
103116
@PatchMapping("/move")
104-
public ResponseEntity<?> moveMany(@Valid @RequestBody reqBodyForMoveMany rq) {
105-
Integer currentMemberId = StubAuthUtil.currentMemberId();
117+
public ResponseEntity<?> moveMany(
118+
@Valid @RequestBody reqBodyForMoveMany rq,
119+
@AuthenticationPrincipal CustomUserDetails userDetails
120+
) {
121+
Member member = userDetails.getMember();
122+
Integer currentMemberId = member.getId();
106123

107124
dataSourceService.moveDataSources(currentMemberId, rq.folderId(), rq.dataSourceId());
108125

@@ -122,7 +139,8 @@ public ResponseEntity<?> moveMany(@Valid @RequestBody reqBodyForMoveMany rq) {
122139
@PatchMapping("/{dataSourceId}")
123140
public ResponseEntity<?> updateDataSource(
124141
@PathVariable Integer dataSourceId,
125-
@Valid @RequestBody reqBodyForUpdateDataSource body
142+
@Valid @RequestBody reqBodyForUpdateDataSource body,
143+
@AuthenticationPrincipal CustomUserDetails userDetails
126144
) {
127145
// title, summary 둘 다 비어있으면 의미 없는 요청 → 400
128146
boolean noTitle = (body.title() == null || body.title().isBlank());
@@ -131,7 +149,8 @@ public ResponseEntity<?> updateDataSource(
131149
throw new IllegalArgumentException("변경할 값이 없습니다. title 또는 summary 중 하나 이상을 전달하세요.");
132150
}
133151

134-
Integer updatedId = dataSourceService.updateDataSource(dataSourceId, body.title(), body.summary());
152+
Member member = userDetails.getMember();
153+
Integer updatedId = dataSourceService.updateDataSource(member.getId(), dataSourceId, body.title(), body.summary()); // CHANGED
135154
String msg = updatedId + "번 자료가 수정됐습니다.";
136155
return ResponseEntity.ok(
137156
new ApiResponse<>(200, msg, new resBodyForUpdateDataSource(updatedId))

src/main/java/org/tuna/zoopzoop/backend/domain/datasource/dto/FileSummary.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22

33
import org.tuna.zoopzoop.backend.domain.datasource.entity.Tag;
44

5-
import java.time.LocalDateTime;
5+
import java.time.LocalDate;
66
import java.util.List;
77

88
public record FileSummary(
99
Integer dataSourceId,
1010
String title,
11-
LocalDateTime createdAt,
11+
LocalDate createdAt,
1212
String summary,
1313
String sourceUrl,
1414
String imageUrl,
15-
List<Tag> tags
15+
List<Tag> tags,
16+
String category
1617
) {}

src/main/java/org/tuna/zoopzoop/backend/domain/datasource/repository/DataSourceRepository.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,35 @@
99

1010
import java.util.Collection;
1111
import java.util.List;
12+
import java.util.Optional;
1213

1314
@Repository
1415
public interface DataSourceRepository extends JpaRepository<DataSource, Integer> {
1516
List<DataSource> findAllByFolder(Folder folder);
1617

17-
@Query("select d.id from DataSource d where d.id in ?1")
18-
java.util.List<Integer> findExistingIds(Collection<Integer> ids);
19-
2018
List<DataSource> findAllByIdIn(Collection<Integer> ids);
19+
20+
// CHANGED: 특정 멤버(개인 아카이브 소유자) 범위에서 id로 조회 (ownership check)
21+
@Query("""
22+
select d from DataSource d
23+
join d.folder f
24+
join f.archive a
25+
join PersonalArchive pa on pa.archive = a
26+
where d.id = :id
27+
and pa.member.id = :memberId
28+
""")
29+
Optional<DataSource> findByIdAndMemberId(@Param("id") Integer id, @Param("memberId") Integer memberId);
30+
31+
// CHANGED: 여러 id 중에서 해당 member 소유인 id만 반환 (다건 삭제/검증용)
32+
@Query("""
33+
select d.id from DataSource d
34+
join d.folder f
35+
join f.archive a
36+
join PersonalArchive pa on pa.archive = a
37+
where pa.member.id = :memberId
38+
and d.id in :ids
39+
""")
40+
List<Integer> findExistingIdsInMember(@Param("memberId") Integer memberId, @Param("ids") Collection<Integer> ids);
41+
2142
}
2243

0 commit comments

Comments
 (0)