Skip to content

Commit

Permalink
Merge pull request #87 from modern-agile-team/feature/board-images
Browse files Browse the repository at this point in the history
Refactor(2swo/Board-Images) 보드 이미지 수정 추가(1차) , s3로직 변경
  • Loading branch information
2swo authored Nov 2, 2023
2 parents 2941968 + ca6b786 commit 3e1c86b
Show file tree
Hide file tree
Showing 15 changed files with 153 additions and 68 deletions.
12 changes: 8 additions & 4 deletions src/boards/controllers/Boards.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import { ApiAddBoard } from '../swagger-decorators/add-board-decorators';
import { ApiGetPageBoards } from '../swagger-decorators/get-page-boards-decorators';
import { ApiGetOneBoard } from '../swagger-decorators/get-one-board-decorators';
import { ApiUpdateBoard } from '../swagger-decorators/patch-board-decorators';
import { ApiTags } from '@nestjs/swagger';
import { ApiDeleteBoard } from '../swagger-decorators/delete-board-decorators';

@Controller('boards')
@ApiTags('board API')
export class BoardsController {
constructor(
private readonly boardsService: BoardsService,
Expand Down Expand Up @@ -94,19 +97,20 @@ export class BoardsController {
async editBoardImages(
@Headers('access_token') accessToken: string,
@Query('boardId') boardId: number,
@Body() formData: FormData, // 재진이가 보낸 FormData
@Query('deleteImageUrl') deleteImageUrl: string,
@UploadedFiles() files: Express.Multer.File[],
) {
const userId = await this.tokenService.decodeToken(accessToken);
console.log(formData);

return await this.boardImagesService.updateBoardImages(
boardId,
formData,
files,
userId,
deleteImageUrl,
);
}

@Delete('')
@ApiDeleteBoard()
async deleteBoard(
@Query('boardId') boardId: number,
@Headers('access_token') accessToken: string,
Expand Down
4 changes: 1 addition & 3 deletions src/boards/entities/board-image.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ export class BoardImage {
@PrimaryGeneratedColumn()
id: number;

@ManyToOne(() => Board, (board) => board.boardImages, {
onDelete: 'CASCADE',
})
@ManyToOne(() => Board, (board) => board.boardImages)
@JoinColumn({ name: 'board_id' })
board: Board;

Expand Down
11 changes: 8 additions & 3 deletions src/boards/entities/board.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Column,
CreateDateColumn,
Entity,
Index,
JoinColumn,
ManyToOne,
OneToMany,
Expand All @@ -26,18 +27,22 @@ export class Board {
@JoinColumn({ name: 'user_id' })
user: User;

@OneToMany(() => BoardImage, (boardImage) => boardImage.board)
@OneToMany(() => BoardImage, (boardImage) => boardImage.board, {
onDelete: 'CASCADE',
})
boardImages: BoardImage[];

@OneToMany(() => BoardLike, (boardLike) => boardLike.boardId, {
onDelete: 'CASCADE',
})
boardLike: BoardLike;

@Column()
@Index({ fulltext: true })
@Column('varchar')
head: string;

@Column()
@Index({ fulltext: true })
@Column('text')
body: string;

@Column()
Expand Down
12 changes: 3 additions & 9 deletions src/boards/repository/boardImage.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,8 @@ export class BoardImageRepository {
return this.entityManager.find(BoardImage, { where: { boardId } });
}

async createBoardImage(boardImage: CreateBoardImageDto): Promise<BoardImage> {
const newImage = new BoardImage();
newImage.boardId = boardImage.boardId;
newImage.imageUrl = boardImage.imageUrl;
return this.entityManager.save(newImage);
}

async deleteBoardImage(imageId: number): Promise<void> {
await this.entityManager.delete(BoardImage, imageId);
async deleteImages(imagesToDelete: BoardImage[]): Promise<void> {
const imageIds = imagesToDelete.map((image) => image.id);
await this.entityManager.delete(BoardImage, imageIds);
}
}
5 changes: 4 additions & 1 deletion src/boards/repository/boards.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export class BoardRepository {
});
}

async updateBoard(id: number, boardData: Partial<Board>): Promise<Board> {
async updateBoard(
id: number,
boardData: Partial<CreateBoardDto>,
): Promise<Board> {
const existingBoard = await this.entityManager.findOne(Board, {
relations: ['user', 'user.userImage', 'boardImages'],
where: { id },
Expand Down
64 changes: 36 additions & 28 deletions src/boards/services/BoardImage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ export class BoardImagesService {
): Promise<CreateBoardImageDto[]> {
const savedImagesArray: CreateBoardImageDto[] = [];
for (const file of files) {
const uploadedImage = await this.s3Service.BoardImageUpload(file, userId);
const uploadedImage = await this.s3Service.uploadImage(
file,
userId,
'BoadImages/',
);
const boardImage = new CreateBoardImageDto();
boardImage.boardId = boardId;
boardImage.imageUrl = uploadedImage.url;
Expand All @@ -30,38 +34,42 @@ export class BoardImagesService {

async updateBoardImages(
boardId: number,
formData: FormData,
files: Express.Multer.File[],
userId: number,
): Promise<CreateBoardImageDto[]> {
const currentImages =
deleteImageUrl: string,
): Promise<any> {
const existingImages =
await this.boardImageRepository.getBoardImages(boardId);
const updatedImages: CreateBoardImageDto[] = [];

for (const [key, value] of formData.entries()) {
if (key === 'files') {
if (typeof value === 'string' && value.startsWith('http')) {
// value값이 URL인 경우, DB와 일치하는지 확인
const existingImage = currentImages.find(
(image) => image.imageUrl === value,
);
if (existingImage) {
updatedImages.push(existingImage);
}
} else {
const file = value as File; // 이미지 파일인 경우
const fileUrl = await this.s3Service.BoardImageUpload(file, userId);
const imagesToDelete = existingImages.filter(
(image) => image.imageUrl === deleteImageUrl,
);
const s3ToDelete = imagesToDelete.map((image) => {
const parts = image.imageUrl.split('/');
const fileName = parts[parts.length - 1];
return 'BoardImages/' + fileName;
});

const newImage = new CreateBoardImageDto();
newImage.boardId = boardId;
newImage.imageUrl = fileUrl.url;
await this.boardImageRepository.deleteImages(imagesToDelete);
await this.s3Service.deleteImage(s3ToDelete.join(','));

const savedImage =
await this.boardImageRepository.createBoardImage(newImage);
updatedImages.push(savedImage);
}
}
const newImagesArray: CreateBoardImageDto[] = [];
for (const file of files) {
const uploadedImage = await this.s3Service.uploadImage(
file,
userId,
'BoardImages/',
);
const boardImage = new CreateBoardImageDto();
boardImage.boardId = boardId;
boardImage.imageUrl = uploadedImage.url;
const savedImage =
await this.boardImageRepository.saveBoardImage(boardImage);
newImagesArray.push(savedImage);
}

return updatedImages;
return {
message: '이미지 업데이트 및 삭제가 성공적으로 처리되었습니다.',
newImagesArray,
};
}
}
2 changes: 1 addition & 1 deletion src/boards/services/Boards.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class BoardsService {

async updateBoard(
boardId: number,
boardData: Partial<Board>,
boardData: Partial<CreateBoardDto>,
): Promise<Board | undefined> {
const existingBoard = await this.boardRepository.findBoardById(boardId);
for (const key in boardData) {
Expand Down
13 changes: 12 additions & 1 deletion src/boards/swagger-decorators/add-board-decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@ export function ApiAddBoard() {
status: 200,
description: '성공적으로 보드를 생성한 경우',
content: {
JSON: { example: { message: '보드를 성공적으로 생성하였습니다.' } },
JSON: {
example: {
head: '게시물 제목',
body: '게시물 내용',
main_category: '자유',
sub_category: '잡담',
userId: '유저 아이디가 number로 들어옵니다',
id: '생성된 보드 id number로 들어옵니다',
createAt: '2023-10-29T23:45:54.023Z',
updateAt: '2023-10-29T23:45:54.023Z',
},
},
},
}),
ApiResponse({
Expand Down
36 changes: 36 additions & 0 deletions src/boards/swagger-decorators/delete-board-decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { applyDecorators } from '@nestjs/common';
import { ApiOperation, ApiResponse } from '@nestjs/swagger';

export function ApiDeleteBoard() {
return applyDecorators(
ApiOperation({
summary: '게시글 삭제',
description: 'Header - access-token, Param - board-id',
}),
ApiResponse({
status: 200,
description: '성공적으로 게시글 삭제.',
content: {
JSON: {
example: {
success: true,
msg: '게시글 삭제 성공',
},
},
},
}),
ApiResponse({
status: 404,
description: '게시글을 찾을 수 없는 경우',
content: {
JSON: {
example: {
success: false,
code: 404,
data: '존재하지 않는 게시물입니다.',
},
},
},
}),
);
}
39 changes: 38 additions & 1 deletion src/boards/swagger-decorators/patch-board-decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,44 @@ export function ApiUpdateBoard() {
status: 200,
description: '보드의 내용을 성공적으로 수정한 경우',
content: {
JSON: { example: { message: '성공적으로 보드 수정을 완료했습니다.' } },
JSON: {
example: {
id: '보드의 id를 받아옵니다.',
userId: '유저의 id를 받아옵니다.',
head: '수정한 게시물 제목입니다.',
body: '수정한 게시물 본문입니다.',
main_category: '수정한 메인 카테고리입니다.',
sub_category: '수정한 서브 카테고리입니다.',
createAt: '2023-10-29T17:07:53.964Z',
updateAt: '수정한 시간이 됩니다.',
user: {
name: '이승우',
userImage: {
id: '유저이미지 고유 id가 number로 들어옵니다',
userId: '유저의 고유 id가 number로 들어옵니다',
imageUrl:
'유저의 고유 프로필 사진 URL이 string으로 들어옵니다.',
},
},
boardImages: [
{
id: '고유 보드 이미지 id',
boardId: '수정한 보드의 id',
imageUrl: 's3에 저장된 보드 이미지 URL',
},
{
id: '고유 보드 이미지 id',
boardId: '수정한 보드의 id',
imageUrl: 's3에 저장된 보드 이미지 URL',
},
{
id: '고유 보드 이미지 id',
boardId: '수정한 보드의 id',
imageUrl: 's3에 저장된 보드 이미지 URL',
},
],
},
},
},
}),
ApiResponse({
Expand Down
2 changes: 1 addition & 1 deletion src/chat/services/chat.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export class ChatService {
throw new NotFoundException('채팅을 전송할 유저가 채팅방에 없습니다');
}

const imageUrl = await this.s3Service.ChatImageUpload(file, myId);
const imageUrl = await this.s3Service.uploadImage(file, myId, 'ChatImages');

return this.chatRepository.createChatImage(
roomId,
Expand Down
14 changes: 1 addition & 13 deletions src/common/s3/s3.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class S3Service {

private s3Adress = `https://${process.env.AWS_S3_BUCKET}.s3.${process.env.AWS_S3_REGION}.amazonaws.com/`;

private async uploadImage(
async uploadImage(
file,
userId,
folderName,
Expand All @@ -48,18 +48,6 @@ export class S3Service {
}
}

async BoardImageUpload(file, userId): Promise<{ url: string; key: string }> {
return await this.uploadImage(file, userId, 'BoardImages');
}

async UserImageUpload(file, userId): Promise<{ url: string; key: string }> {
return await this.uploadImage(file, userId, 'UserImages');
}

async ChatImageUpload(file, userId): Promise<{ url: string; key: string }> {
return await this.uploadImage(file, userId, 'ChatImages');
}

async deleteImage(key: string): Promise<boolean> {
const params = {
Bucket: process.env.AWS_S3_BUCKET,
Expand Down
1 change: 0 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { AppModule } from './app.module';
import { Logger, ValidationPipe } from '@nestjs/common';
import { setupSwagger } from './config/swagger';
import { AsyncApiDocumentBuilder, AsyncApiModule } from 'nestjs-asyncapi';
import { HttpExceptionFilter } from './http-Exception.filter';

async function bootstrap() {
const app = await NestFactory.create(AppModule, { cors: true });
Expand Down
4 changes: 3 additions & 1 deletion src/users/entities/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
JoinColumn,
OneToMany,
OneToOne,
Index,
} from 'typeorm';
import { UserImage } from './user-image.entity';
import { Token } from 'src/auth/entities/token.entity';
Expand All @@ -24,7 +25,8 @@ export class User {
@Column({ length: 10 })
provider: string;

@Column({ length: 20 })
@Index({ fulltext: true })
@Column('varchar', { length: 20 })
name: string;

@Column({ length: 100 })
Expand Down
2 changes: 1 addition & 1 deletion src/users/services/user-image.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class UserImageService {
file: Express.Multer.File,
): Promise<{ message: string }> {
try {
const res = await this.s3Service.UserImageUpload(file, userId); // S3에 이미지 업로드
const res = await this.s3Service.uploadImage(file, userId, 'UserImages'); // S3에 이미지 업로드
if (!res) {
throw new InternalServerErrorException(
'S3 이미지 업로드에 실패했습니다.',
Expand Down

0 comments on commit 3e1c86b

Please sign in to comment.