Side-project : CKEditor4 + AWS S3 이미지 업로드하기(2)

우진·2023년 5월 4일
1


👾 CKEditor 4

https://ckeditor.com/ckeditor-4/download/


👾 Upload Image To S3 With CKEditor4


여러 시행착오를 겪으며 코드를 짰는데 완벽하지는 않다.
우선, 제일 큰 단점.. S3 서버에 이미지 파일 업로드를 하긴 하는데 CKEditor4에 다시 뿌려지는 이미지를 제거해도 서버 리소스가 삭제되지 않는다 (;^◇^;)ゝ ㅋ.ㅋ..
이건 CKEditor4를 자세히 뜯어봐야할 것 같은데, 여전히 할 게 산더미인 관계로 나중에 다시 고민해보기로 했다.!


(1) 📁 AWS S3 Info Setting

지난번appliation.properties에 이미지 패스 설정을 해줬었는데, 여기에 그대로 추가하면 된다.

(이전 코드)
#FILE_UPLOAD
resource.handler = /ck/images/**
resource.location = file:///C:/2023_side/imgUpload/

image.upload.path = C:/2023_side/imgUpload

⬇ ⬇

(이렇게 변경!)
resource.handler = /upload-image/**

#S3_IAM
cloud.aws.credentials.accessKey= {IAM 계정 Access Key}
cloud.aws.credentials.secretKey= {IAM 계정 Secret Key}
cloud.aws.stack.auto=false

#S3_SERVICE_BUCKET
cloud.aws.s3.bucket={설정한 버킷 이름}
cloud.aws.region.static=ap-northeast-2(한국, 서울)

#S3_BUCKET_URL
cloud.aws.s3.bucket.url=https://{설정한 버킷 이름}.s3.ap-northeast-2.amazonaws.com

(2) 📁 build.gradle Setting

AWS S3 사용을 위한 gradle 의존성을 추가해야 한다. 추가하고서 코끼리 아이콘 눌러 재빌드 필수!

implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.6.RELEASE'

신규 버전 확인https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-aws 이 페이지에서 하면 된다.


(3) 📁 AwsConfig Setting

AwsConfig라는 클래스를 새로 만들고 아래처럼 코드를 작성한다.

@Configuration
public class AwsConfig {

    @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 amazonS3() {
        AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
        return AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
                .build();
    }
}

(4) 📁 WebConfig Setting

마찬가지로, 이전 포스팅에서 한 번 다뤘었는데 핸들링해야하는 경로가 바뀌었으니 다시 설정해야 한다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Value("${resource.handler}")
    private String resourceHandler;

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

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(resourceHandler)
                .addResourceLocations(resourceLocation);
    }

}

resourceHandler에서 설정한 URL로 요청이 올 경우, resourceLocation으로 대체되는 로직이다.


(5) 📁 Create S3FileService

이전에는 컨트롤러만 만들었는데 이번에는 서비스를 분리해볼 거다.

@Service
@RequiredArgsConstructor
public class S3FileService {

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

    private final AmazonS3 amazonS3;

    public String s3FileUpload(MultipartFile multipartFile, String dir) throws IOException {
        //파일명 설정 : {UUID}_{경로}.{extension}
        String extension = FilenameUtils.getExtension(multipartFile.getOriginalFilename());
        String s3FileName = UUID.randomUUID() + "_" + dir + "." + extension;
        //파일 저장경로 설정
        String uploadFilePath = dir + "/";

        //파일 사이즈 설정
        ObjectMetadata objMeta = new ObjectMetadata();
        objMeta.setContentLength(multipartFile.getSize());

        String keyName = uploadFilePath + s3FileName;

        //파일 업로드
        // 외부에 공개하는 파일인 경우 Public Read 권한을 추가, ACL 확인
        amazonS3.putObject(new PutObjectRequest(bucketName, keyName, multipartFile.getInputStream(), objMeta)
                .withCannedAcl(CannedAccessControlList.PublicRead));


        return amazonS3.getUrl(bucketName, keyName).toString();
    }
}
  • String dir:
    메소드 매개변수로 받는 dir은 말그대로 이미지가 업로드 될 파일 경로 다. CKEditor4 외에도, 자체 프로필 업로드를 구현할 예정이라 확장성을 위해 미리 경로를 분리했다.
  • String s3FileName:
    파일명은 { UUID }_{ 경로 }.{ extension } 이런 형태이다.
  • .withCannedAcl(CannedAccessControlList.PublicRead)):
    이걸 붙여야 S3에 업로드된 이미지를 외부에서 읽을 수가 있다.
  • return amazonS3.getUrl(bucketName, keyName).toString():
    S3에 업로드된 파일의 URL을 불러와 리턴해준다.

(6) 📁 Update FileUploadController

@Slf4j
@Controller
@RequestMapping("/boards")
@RequiredArgsConstructor
public class FileUploadController {

    @Autowired
    S3FileService s3FileService;

    @ResponseBody
    @PostMapping("/ck/imageUpload.do")
    public void ckFileUpload(HttpServletResponse res,
                             @RequestParam MultipartFile upload) {

        log.info("컨 트 롤 러 진 입 ! ! ! !");

        PrintWriter printWriter = null;

        try {
            //s3 파일 업로드
            //s3CkUploadPath = {location}/ck/{uid}_ck.{ext}
            String s3CkUploadPath = s3FileService.s3FileUpload(upload, "ck");

            log.info("s3CkUploadPath : {}", s3CkUploadPath);

            //파일 크기
            byte[] bytes = upload.getBytes();
            //원본 파일명
            String originFileName = upload.getOriginalFilename();
            //저장 파일명
            String savedFileName = s3CkUploadPath.substring(s3CkUploadPath.lastIndexOf("/") + 1);

            //ckEditor 로 전송
            printWriter = res.getWriter();
            String fileUrl = "/upload-image/" + "ck/" + savedFileName;

            log.info("fileUrl : {}", fileUrl);

            //json 으로 변환
            JsonObject json = new JsonObject();
            json.addProperty("uploaded", 1);
            json.addProperty("fileName", originFileName);
            json.addProperty("url", fileUrl);
            printWriter.println(json);
            printWriter.flush(); //초기화

            log.info("json : {}", json);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(printWriter != null) { printWriter.close(); }
        }
    }

}
  • String s3CkUploadPath = s3FileService.s3FileUpload(upload, "ck"):
    실제 이미지 파일을 업로드하는 로직이다. s3CkUploadPath에는 서비스에서 반환된 실제 저장 경로가 담긴다.
  • String savedFileName:
    코드를 다 짜고나서 좀 후회했는데.. 차라리 S3FileDTO 객체를 만들어 모든 정보를 한 번에 반환했으면 더 깔끔했을 것같다.(*☉౪ ⊙。) .. 아무튼, 이 로직은 반환된 URL에서 저장된 이미지 파일명을 분리해주는 로직이다.
  • String fileUrl = "/upload-image/" + "ck/" + savedFileName:
    WebConfig에서 설정해둔대로 /upload-image/ 하위 경로로 오는 요청은 전부 본래 URL로 대체된다.

(7) 📁 Use

profile
백 개발을 시작한 응애개발자

2개의 댓글

comment-user-thumbnail
2023년 5월 9일

cke에 s3를 붙이나요? 신기하네용

1개의 답글