본문 바로가기

개발 일지

[TIL]이노베이션 캠프 28일차

 

Spring 심화 주차 8.26(금) ~ 9.1(목)

1. 개인 과제: Spring 핵심 키워드 정리
2. 팀 과제: 요구사항에 맞추어 API 구성 및 Git을 활용한 협업 

 

진행 계획

- 26일(금) API 명세서 작성, ERD 만들기

- 27일(토) 팀 과제에 필요한 강의 수강 및 공부

- 28일(일) 팀 과제 내가 맡은 이미지 업로드 기능 코드 작성

- 29일(월) 개인 과제 Spring 핵심 키워드 정리

- 30일(화) 팀 과제 코드 수정 및 검토

- 31일(수) 팀 과제 코드 취합 및 요구사항 충족 확인

- 1일(목) 팀 과제 코드 최종 취합 및 AWS 배포

 

 

1. 개발 진행 상황

1)  인텔리제이(IntelliJ IDEA)-AWS 연결 하기

👉🏻 인텔리제이 공식 블로그 보고 따라함

 

2) 이미지 업로드 코드 구현

- S3 configuration 

@Configuration
public class S3Config {
    @Value("${cloud.aws.credentials.accessKey}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secretKey}")
    private String secretKey;

    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public AmazonS3 amazonS3Client() {
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

        return AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withRegion(region)
                .build();
    }
}

- S3Uploader(Service)

MultipartFile을 받아서 File 전환 후 S3에 업로드

@Slf4j
@RequiredArgsConstructor    // final 멤버변수가 있으면 생성자 항목에 포함시킴
@Component
@Service
public class S3Uploader {
    private final AmazonS3Client amazonS3Client;

    @Value("${cloud.aws.s3.bucket}")
    private String bucket;

    // MultipartFile을 전달받아 File로 전환한 후 S3에 업로드
       public String upload(MultipartFile multipartFile, String dirName) throws IOException {
        File uploadFile = convert(multipartFile)
                .orElseThrow(() -> new IllegalArgumentException("fail convert multipart to file"));
        return upload(uploadFile, dirName);
    }

    private String upload(File uploadFile, String dirName) {
        String fileName = dirName + "/" + uploadFile.getName();
        String uploadImageUrl = putS3(uploadFile, fileName);

        removeNewFile(uploadFile);  // 로컬에 생성된 File 삭제 (MultipartFile -> File 전환 하며 로컬에 파일 생성됨)

        return uploadImageUrl;      // 컨트롤러로 S3 URL 주소 반환
    }

    private String putS3(File uploadFile, String fileName) {
        amazonS3Client.putObject(
                new PutObjectRequest(bucket, fileName, uploadFile)
                        .withCannedAcl(CannedAccessControlList.PublicRead)	// PublicRead 권한으로 업로드 됨
        );
        return amazonS3Client.getUrl(bucket, fileName).toString(); // 업로드된 파일의 S3 URL 주소 반환
    }

    private void removeNewFile(File targetFile) {
        if(targetFile.delete()) {
            log.info("파일이 삭제되었습니다.");
        }else {
            log.info("파일이 삭제되지 못했습니다.");
        }
    }

    private Optional<File> convert(@NotNull MultipartFile file) throws  IOException {
        File convertFile = new File(file.getOriginalFilename());
        if(convertFile.createNewFile()) {
            try (FileOutputStream fos = new FileOutputStream(convertFile)) {
                fos.write(file.getBytes());
            }
            return Optional.of(convertFile);
        }
        return Optional.empty();
    }

}

 

- ImageController

MultipartFile을 받아서 S3Uploader를 통해 업로드 및 이미지URL을 반환 받고 그 주소를 요구사항에 맞게 응답해주기 위해서 imageService를 이용함

@RestController
@RequiredArgsConstructor
public class ImageController {
    private final S3Uploader s3Uploader;
    private final ImageService imageService;

    @RequestMapping(value = "/api/upload/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, method = RequestMethod.POST)
    public ResponseDto<?> upload(HttpServletRequest request, @RequestParam(value="image", required = false) MultipartFile multipartFile) throws IOException {

        String imgUrl = s3Uploader.upload(multipartFile, "static");

        return imageService.upload(request, imgUrl);
    }
}

 

- ImageService

@Service
@RequiredArgsConstructor
public class ImageService {
    private final PostRepository postRepository;
    private final ImageRepository imageRepository;
    private final TokenProvider tokenProvider;


    @Transactional
    public ResponseDto<?> upload(HttpServletRequest request, String imgUrl) {
        if (null == request.getHeader("Refresh-Token")) {
            return ResponseDto.fail("MEMBER_NOT_FOUND",
                    "로그인이 필요합니다.");
        }

        if (null == request.getHeader("Authorization")) {
            return ResponseDto.fail("MEMBER_NOT_FOUND",
                    "로그인이 필요합니다.");
        }

        Member member = validateMember(request);
        if (null == member) {
            return ResponseDto.fail("INVALID_TOKEN", "Token이 유효하지 않습니다.");
        }

        if (null == imgUrl) {
            return ResponseDto.fail("EMPTY", "multipart file is empty");
        }

        Image image = Image.builder()
                .imgUrl(imgUrl)
                .build();
        imageRepository.save(image);
        return ResponseDto.success(
                ImageResponseDto.builder()
                .data(image.getImgUrl())
                .build()
        );
    }
    @Transactional
    public Member validateMember(HttpServletRequest request) {
        if (!tokenProvider.validateToken(request.getHeader("Refresh-Token"))) {
            return null;
        }
        return tokenProvider.getMemberFromAuthentication();
    }
}

 

2. 오늘 한 일 / 회고

- 팀 과제: 이미지 업로드 기능 구현

- 강의 수강

 

3. TO-DO LIST

- 개인 과제: 스프링 키워드 작성

- 강의 수강

 

4. 레퍼런스

https://docs.aws.amazon.com/cli/latest/reference/s3/cp.html

 

cp — AWS CLI 1.25.62 Command Reference

Note: You are viewing the documentation for an older major version of the AWS CLI (version 1). AWS CLI version 2, the latest major version of AWS CLI, is now stable and recommended for general use. To view this page for the AWS CLI version 2, click here. F

docs.aws.amazon.com