S3와 호환되므로 AWS-SDK 모듈 등에서 Endpoint를 kr.object.ncloudstorage.com
으로 설정해서 사용하면 된다.
import * as AWS from 'aws-sdk';
import { configDotenv } from 'dotenv';
configDotenv();
export const awsConfig = {
endpoint: new AWS.Endpoint(process.env.AWS_S3_ENDPOINT),
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
};
export const bucketName = process.env.AWS_BUCKET_NAME;
// NCP Object Storage 업로드
AWS.config.update(awsConfig);
const result = await new AWS.S3()
.putObject({
Bucket: bucketName,
Key: filename,
Body: buffer,
ACL: 'public-read',
})
.promise();
Logger.log('uploadFile result:', result);
// NCP Object Storage 다운로드
AWS.config.update(awsConfig);
const result = await new AWS.S3()
.getObject({
Bucket: bucketName,
Key: filename,
})
.promise();
Logger.log(`downloadFile result: ${result.ETag}`);
NCloud 공식문서에 나와있는 S3 Endpoint 주소가 문제였다..
여기 나와있는 주소를 썼는데 ACCESS KEY가 없다고 나와 뭔가 잘못한 줄 알고 한참을 헤맸는데
혹시나 싶어 다른 문서에 나와있는
이 주소를 보니까 다른거야.. 그래서 이걸로 했더니 됨. ㅋ
알고보니 내가 보던 공식문서는 financial용 ncloud로 서버도 인증키도 따로 관리되는 거였다... 내가 바보 ㅇㅈ
이제 주석처리해 두었던 업로드 로직을 다시 추가해줬다.
async createBoard(
createBoardDto: CreateBoardDto,
userData: UserDataDto,
files: Express.Multer.File[],
): Promise<Board> {
const { title, content } = createBoardDto;
const user = await this.userRepository.findOneBy({ id: userData.userId });
const images: Image[] = [];
for (const file of files) {
const image = await this.uploadFile(file);
images.push(image);
}
const board = this.boardRepository.create({
title,
content: encryptAes(content), // AES 암호화하여 저장
user,
images,
});
const createdBoard: Board = await this.boardRepository.save(board);
createdBoard.user.password = undefined; // password 제거하여 반환
return createdBoard;
}
파일 목록에서 순서대로 읽어와 uploadFile() 호출
async uploadFile(file: Express.Multer.File): Promise<Image> {
if (!file.mimetype.includes('image')) {
throw new BadRequestException('Only image files are allowed');
}
const { mimetype, buffer, size } = file;
const filename = uuid();
// NCP Object Storage 업로드
AWS.config.update(awsConfig);
const result = await new AWS.S3()
.putObject({
Bucket: bucketName,
Key: filename,
Body: buffer,
ACL: 'public-read',
})
.promise();
Logger.log('uploadFile result:', result);
const updatedImage = await this.imageRepository.save({
mimetype,
filename,
size,
});
return updatedImage;
}
업로드 파일 로직은 uuid로 생성된 식별자를 파일이름으로 Object Storage에 업로드하고,
나머지 파일에 대한 정보는 관계형 DB 이미지 테이블에 저장한다.
yarn workspace server add form-data
폼데이터로 응답을 해야하기 때문에 form-data 모듈을 설치해줬다.
async downloadFile(filename: string): Promise<Buffer> {
// NCP Object Storage 다운로드
AWS.config.update(awsConfig);
const result = await new AWS.S3()
.getObject({
Bucket: bucketName,
Key: filename,
})
.promise();
Logger.log(`downloadFile result: ${result.ETag}`);
return result.Body as Buffer;
}
@Get(':id')
@UseGuards(CookieAuthGuard)
async findBoardById(
@Param('id', ParseIntPipe) id: number,
@Res() res,
): Promise<void> {
const found = await this.boardService.findBoardById(id);
// AES 복호화
if (found.content) {
found.content = decryptAes(found.content); // AES 복호화하여 반환
}
// 폼 데이터 만들어 반환
const formData = new FormData();
formData.append('id', found.id.toString());
formData.append('title', found.title);
formData.append('content', found.content);
formData.append('author', found.user.nickname);
formData.append('created_at', found.created_at.toString());
formData.append('updated_at', found.updated_at.toString());
formData.append('like_cnt', found.like_cnt.toString());
// NCP Object Storage 다운로드
const files = [];
for (let image of found.images) {
const file: Buffer = await this.boardService.downloadFile(image.filename);
console.log(file);
formData.append('file', file, {
filename: image.filename,
contentType: image.mimetype,
});
}
res.set({
'Content-Type': 'multipart/form-data',
});
formData.pipe(res);
// return found;
}
다운로드는 컨트롤러에서 개별 파일 다운로드하는 서비스 메소드인 downloadFile을 호출해서 순서대로 폼데이터에 넣고,
@Res()
데코레이터로 가져온 Response 객체에 전달한다.