Skip to content

Commit

Permalink
[REFACTOR] 파일 시스템 구조 리팩토링 및 파일 API 분리 (#165)
Browse files Browse the repository at this point in the history
* feat: 파일 핵심 로직을 지니는 인터페이스 및 구체클래스 생성

* refactor: 파일 시스템 구조 개편

- FileUtil에는 핵심 비지니스 로직이 아닌, 유틸성 메서드로 구성
- FileManager 인터페이스 생성 및 상속을 통해 확장성 있돌고 변경
- yml의 값을 통해 로컬/배포 환경에 따라 각자 다른 파일 시스템을 사용하도록 설정

* test: 파일 시스템 개편으로 인한 테스트 코드 수정

* refactor: 개편한 파일 시스템 코드 리팩토링

- 필요없는 코드 삭제
- DTO의 이름을 포괄적으로 변경

* chore: 파일 시스템 관련 주석 정리

* refactor: 메서드명 변경 및 필요없는 메서드 삭제

* feat: FileManager의 기능을 별도로 테스트하기 위한 컨트롤러 생성

* refactor: 파일 홀더 인터페이스 생성 및 적용

- FileHolder 인터페이스 생성
- 파일과 연관관계를 맺는 User, Topic, Instance 객체에 대해 FileHolder 인터페이스 적용

* feat: 파일 조회 테스트 API 추가

- FileTestController에 GET 요청을 통해 등록되어 있는 파일을 조회하는 테스트 목적 API 추가

* refactor: 파일 생성 API 분리

- 회원가입, 토픽 생성, 인스턴스 생성 API에 대해 분리작업 진행
- FileHolder 인터페이스에 파일을 받아오는 getFiles() 메서드 추가 & 메서드 이름 변경
- 로직 변경으로 인한 테스트 코드 변경

* refactor: 파일 수정 API 분리

- �토픽 수정, 인스턴스 수정 API에 대해 분리 작업 진행
- 파일 조회 API 분리
- 로직 변경으로 인한 테스트 코드 변경

* �fix: Querydsl 빌드 시 오류나는 부분 수정

* feat: MultipartFile이 오지 않는 경우에 대비하여 예외 처리 추가

* refactor: Topic쪽 API에 대해 리팩토링

- 응답 DTO에서 FileResponse 적용 로직 변경

* refactor: Instance쪽 API 리팩토링

- 응답 DTO에서 FileResponse 적용 로직 변경

* refactor: 좋아요 관련 API 리팩토링

- 응답 DTO에서 FileResponse 적용 로직 변경

* refactor: 마이챌린지 관련 API 리팩터링

- 응답 DTO에서 FileResponse 적용 로직 변경

* refactor: 유저 프로필 관련 API 리팩토링

- 응답 DTO에서 FileResponse 적용 로직 변경

* refactor: 인증 조회 API 리팩터링

- 응답 DTO에서 FileResponse 적용 로직 변경

* chore: Github actions 테스트 workflow 작성

- 특정 브랜치 빌드 진행했을 때, build에 문제가 생기지 않는지 확인하는 확인하기 위한 yml 파일 생성
- gradlew를 통한 test build까지만 진행하도록 설정

* test: 파일과 관련된 테스트 코드 수정

* test: 테스트 이후 mongoDB 초기화 코드 추가

* refactor: File 관련 요청할 수 있도록 응답 객체 설정
  • Loading branch information
SSung023 authored Apr 18, 2024
1 parent 3c0707b commit c0cd332
Show file tree
Hide file tree
Showing 70 changed files with 973 additions and 645 deletions.
50 changes: 50 additions & 0 deletions .github/workflows/test-task.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Build test about test codes

on:
push:
branches: [ "refactor/162-file-system-structure" ] ## 테스트하고자하는 브랜치 작성
pull_request:
branches: [ "refactor/162-file-system-structure" ]

jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: make application.yml
run: |
mkdir -p ./src/main/resources
cd ./src/main/resources
touch ./application.yml
touch ./application-common.yml
touch ./application-prod.yml
echo "${{ secrets.APPLICATION }}" > ./application.yml
echo "${{ secrets.COMMON }}" > ./application-common.yml
echo "${{ secrets.PROD }}" > ./application-prod.yml
- name: make test application.yml
run: |
mkdir -p ./src/test/resources
cd ./src/test/resources
touch ./application.yml
touch ./application-test.yml
echo "${{ secrets.APPLICATION_TEST }}" > ./application.yml
echo "${{ secrets.TEST }}" > ./application-test.yml
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash

- name: Build and Test
run: ./gradlew clean build test

Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.genius.gitget.admin.topic.controller;

import static com.genius.gitget.global.util.exception.SuccessCode.CREATED;
import static com.genius.gitget.global.util.exception.SuccessCode.SUCCESS;

import com.genius.gitget.admin.topic.dto.TopicCreateRequest;
import com.genius.gitget.admin.topic.dto.TopicDetailResponse;
import com.genius.gitget.admin.topic.dto.TopicIndexResponse;
import com.genius.gitget.admin.topic.dto.TopicPagingResponse;
import com.genius.gitget.admin.topic.dto.TopicUpdateRequest;
import com.genius.gitget.admin.topic.service.TopicService;
import com.genius.gitget.global.util.exception.SuccessCode;
import com.genius.gitget.global.util.response.dto.CommonResponse;
import com.genius.gitget.global.util.response.dto.PagingResponse;
import com.genius.gitget.global.util.response.dto.SingleResponse;
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -21,10 +23,9 @@
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequiredArgsConstructor
Expand All @@ -40,40 +41,42 @@ public ResponseEntity<PagingResponse<TopicPagingResponse>> getAllTopics(
Page<TopicPagingResponse> allTopics = topicService.getAllTopics(pageable);

return ResponseEntity.ok().body(
new PagingResponse<>(SuccessCode.SUCCESS.getStatus(), SuccessCode.SUCCESS.getMessage(), allTopics)
new PagingResponse<>(SUCCESS.getStatus(), SUCCESS.getMessage(), allTopics)
);
}

// 토픽 상세 정보 요청
@GetMapping("/{id}")
public ResponseEntity<SingleResponse<TopicDetailResponse>> getTopicById(@PathVariable Long id) throws IOException {
public ResponseEntity<SingleResponse<TopicDetailResponse>> getTopicById(@PathVariable Long id) {
TopicDetailResponse topicDetail = topicService.getTopicById(id);
return ResponseEntity.ok().body(
new SingleResponse<>(SuccessCode.SUCCESS.getStatus(), SuccessCode.SUCCESS.getMessage(), topicDetail)
new SingleResponse<>(SUCCESS.getStatus(), SUCCESS.getMessage(), topicDetail)
);
}

// 토픽 생성 요청
@PostMapping
public ResponseEntity<CommonResponse> createTopic(
@RequestPart(value = "data") TopicCreateRequest topicCreateRequest,
@RequestPart(value = "files", required = false) MultipartFile multipartFile,
@RequestPart(value = "type") String type) {
topicService.createTopic(topicCreateRequest, multipartFile, type);
public ResponseEntity<SingleResponse<TopicIndexResponse>> createTopic(
@RequestBody TopicCreateRequest topicCreateRequest) {
Long topicId = topicService.createTopic(topicCreateRequest);
TopicIndexResponse topicUpdateResponse = new TopicIndexResponse(topicId);

return ResponseEntity.ok().body(
new CommonResponse(SuccessCode.CREATED.getStatus(), SuccessCode.CREATED.getMessage())
new SingleResponse<>(
CREATED.getStatus(), CREATED.getMessage(), topicUpdateResponse)
);
}

// 토픽 수정 요청
@PatchMapping("/{id}")
public ResponseEntity<CommonResponse> updateTopic(@PathVariable Long id,
@RequestPart(value = "data") TopicUpdateRequest topicUpdateRequest,
@RequestPart(value = "files", required = false) MultipartFile multipartFile,
@RequestPart(value = "type") String type) {
topicService.updateTopic(id, topicUpdateRequest, multipartFile, type);
public ResponseEntity<SingleResponse<TopicIndexResponse>> updateTopic(
@PathVariable Long id,
@RequestBody TopicUpdateRequest topicUpdateRequest) {
Long topicId = topicService.updateTopic(id, topicUpdateRequest);
TopicIndexResponse topicUpdateResponse = new TopicIndexResponse(topicId);

return ResponseEntity.ok().body(
new CommonResponse(SuccessCode.SUCCESS.getStatus(), SuccessCode.SUCCESS.getMessage())
new SingleResponse<>(SUCCESS.getStatus(), SUCCESS.getMessage(), topicUpdateResponse)
);
}

Expand All @@ -82,7 +85,7 @@ public ResponseEntity<CommonResponse> updateTopic(@PathVariable Long id,
public ResponseEntity<CommonResponse> deleteTopic(@PathVariable Long id) {
topicService.deleteTopic(id);
return ResponseEntity.ok().body(
new CommonResponse(SuccessCode.SUCCESS.getStatus(), SuccessCode.SUCCESS.getMessage())
new CommonResponse(SUCCESS.getStatus(), SUCCESS.getMessage())
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.genius.gitget.admin.topic.domain;

import com.genius.gitget.challenge.instance.domain.Instance;
import com.genius.gitget.global.file.domain.FileHolder;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.util.domain.BaseTimeEntity;
import jakarta.persistence.CascadeType;
Expand All @@ -26,7 +27,7 @@
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "topic")
public class Topic extends BaseTimeEntity {
public class Topic extends BaseTimeEntity implements FileHolder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "topic_id")
Expand Down Expand Up @@ -73,10 +74,12 @@ public void updateNotExistInstance(String title, String description, String tags
this.pointPerPerson = pointPerPerson;
}

@Override
public Optional<Files> getFiles() {
return Optional.ofNullable(this.files);
}

@Override
public void setFiles(Files files) {
this.files = files;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package com.genius.gitget.admin.topic.dto;

import com.genius.gitget.admin.topic.domain.Topic;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.file.dto.FileResponse;
import java.io.IOException;
import java.util.Optional;
import lombok.Builder;

@Builder
public record TopicDetailResponse(Long topicId, String title, String tags,
String description, String notice, int pointPerPerson, FileResponse fileResponse) {
public static TopicDetailResponse createByEntity(Topic topic, Optional<Files> files) throws IOException {
public record TopicDetailResponse(
Long topicId,
String title,
String tags,
String description,
String notice,
int pointPerPerson,
FileResponse fileResponse) {
public static TopicDetailResponse createByEntity(Topic topic, FileResponse fileResponse) {
return TopicDetailResponse.builder()
.topicId(topic.getId())
.title(topic.getTitle())
.tags(topic.getTags())
.description(topic.getDescription())
.notice(topic.getNotice())
.pointPerPerson(topic.getPointPerPerson())
.fileResponse(FileResponse.create(files))
.fileResponse(fileResponse)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.genius.gitget.admin.topic.dto;

public record TopicIndexResponse(
Long topicId
) {
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package com.genius.gitget.admin.topic.dto;

import com.genius.gitget.admin.topic.domain.Topic;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.file.dto.FileResponse;
import java.io.IOException;
import java.util.Optional;
import lombok.Builder;

@Builder
public record TopicPagingResponse(Long topicId, String title, FileResponse fileResponse) {

public static TopicPagingResponse createByEntity(Topic topic, Optional<Files> files) throws IOException {
public static TopicPagingResponse createByEntity(Topic topic, FileResponse fileResponse) {
return TopicPagingResponse.builder()
.topicId(topic.getId())
.title(topic.getTitle())
.fileResponse(FileResponse.create(files))
.fileResponse(fileResponse)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,16 @@
import com.genius.gitget.admin.topic.dto.TopicPagingResponse;
import com.genius.gitget.admin.topic.dto.TopicUpdateRequest;
import com.genius.gitget.admin.topic.repository.TopicRepository;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.file.dto.FileResponse;
import com.genius.gitget.global.file.service.FilesService;
import com.genius.gitget.global.util.exception.BusinessException;
import com.genius.gitget.global.util.exception.ErrorCode;
import java.io.IOException;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
@RequiredArgsConstructor
Expand All @@ -35,28 +32,21 @@ public Page<TopicPagingResponse> getAllTopics(Pageable pageable) {
}

private TopicPagingResponse mapToTopicPagingResponse(Topic topic) {
try {
return TopicPagingResponse.createByEntity(topic, topic.getFiles());
} catch (IOException e) {
throw new BusinessException(e);
}
FileResponse fileResponse = filesService.convertToFileResponse(topic.getFiles());
return TopicPagingResponse.createByEntity(topic, fileResponse);
}

// 토픽 상세정보 요청
public TopicDetailResponse getTopicById(Long id) throws IOException {
Topic topic = topicRepository.findById(id).orElseThrow(() -> new BusinessException(ErrorCode.TOPIC_NOT_FOUND));
return TopicDetailResponse.createByEntity(topic, topic.getFiles());
public TopicDetailResponse getTopicById(Long id) {
Topic topic = topicRepository.findById(id)
.orElseThrow(() -> new BusinessException(ErrorCode.TOPIC_NOT_FOUND));
FileResponse fileResponse = filesService.convertToFileResponse(topic.getFiles());
return TopicDetailResponse.createByEntity(topic, fileResponse);
}

// 토픽 생성 요청
@Transactional
public Long createTopic(TopicCreateRequest topicCreateRequest, MultipartFile multipartFile, String type) {
System.out.println("토픽 생성 요청");
System.out.println(topicCreateRequest.title());
System.out.println(multipartFile.getOriginalFilename());
System.out.println(type);
Files uploadedFile = filesService.uploadFile(multipartFile, type);

public Long createTopic(TopicCreateRequest topicCreateRequest) {
Topic topic = Topic.builder()
.title(topicCreateRequest.title())
.description(topicCreateRequest.description())
Expand All @@ -65,22 +55,16 @@ public Long createTopic(TopicCreateRequest topicCreateRequest, MultipartFile mul
.notice(topicCreateRequest.notice())
.build();

topic.setFiles(uploadedFile);

Topic savedTopic = topicRepository.save(topic);

return savedTopic.getId();
}

// 토픽 업데이트 요청
@Transactional
public void updateTopic(Long id, TopicUpdateRequest topicUpdateRequest, MultipartFile multipartFile, String type) {
Topic topic = topicRepository.findById(id).orElseThrow(() -> new BusinessException(ErrorCode.TOPIC_NOT_FOUND));

Optional<Files> findTopicFile = topic.getFiles();
Long findTopicFileId = findTopicFile.get().getId();

filesService.updateFile(findTopicFileId, multipartFile);
public Long updateTopic(Long id, TopicUpdateRequest topicUpdateRequest) {
Topic topic = topicRepository.findById(id)
.orElseThrow(() -> new BusinessException(ErrorCode.TOPIC_NOT_FOUND));

// 서버에서 한번 더 검사
boolean hasInstance = !topic.getInstanceList().isEmpty();
Expand All @@ -90,7 +74,7 @@ public void updateTopic(Long id, TopicUpdateRequest topicUpdateRequest, Multipar
topic.updateNotExistInstance(topicUpdateRequest.title(), topicUpdateRequest.description(),
topicUpdateRequest.tags(), topicUpdateRequest.notice(), topicUpdateRequest.pointPerPerson());
}
topicRepository.save(topic);
return topicRepository.save(topic).getId();
}

// 토픽 삭제 요청
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ public ResponseEntity<SingleResponse<WeekResponse>> getWeekCertification(
@PathVariable Long instanceId
) {
Participant participant = participantProvider.findByJoinInfo(userPrincipal.getUser().getId(), instanceId);
WeekResponse weekCertification = certificationService.getMyWeekCertifications(
WeekResponse weekResponse = certificationService.getMyWeekCertifications(
participant.getId(), LocalDate.now());

return ResponseEntity.ok().body(
new SingleResponse<>(SUCCESS.getStatus(), SUCCESS.getMessage(), weekCertification)
new SingleResponse<>(SUCCESS.getStatus(), SUCCESS.getMessage(), weekResponse)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,10 @@ public ActivatedResponse passCertification(Long userId, CertificationRequest cer
return passed;
});

return ActivatedResponse.create(instance, certification.getCertificationStatus(), 0,
participant.getRepositoryName());
FileResponse fileResponse = filesService.convertToFileResponse(instance.getFiles());
//TODO: pass 했기 때문에 pass item이 필요없어 numOfPassItem을 0으로 전달하는 것 같음. but, 가독성이 떨어지기 때문에 수정 필요
return ActivatedResponse.create(instance, certification.getCertificationStatus(),
0, participant.getRepositoryName(), fileResponse);
}

private void validatePassCondition(Optional<Certification> optional) {
Expand Down Expand Up @@ -245,7 +247,7 @@ private void validCertificationCondition(Instance instance, LocalDate targetDate

public InstancePreviewResponse getInstancePreview(Long instanceId) {
Instance instance = instanceProvider.findById(instanceId);
FileResponse fileResponse = filesService.getEncodedFile(instance.getFiles());
FileResponse fileResponse = filesService.convertToFileResponse(instance.getFiles());
return InstancePreviewResponse.createByEntity(instance, fileResponse);
}

Expand Down
Loading

0 comments on commit c0cd332

Please sign in to comment.