Post

AWS S3 버킷 설정 및 파일 관리

AWS S3 버킷 설정 및 파일 관리

이전 프로젝트에서 이미지 저장을 S3 버킷으로 진행하였고 이번에도 사용하게 되어 이 김에 정리하게 되었다.


먼저 아래 링크에서 루트 사용자로 로그인 하도록 하자 AWS 콘솔

공식 가이드를 보고 싶으면 Getting started with Amazon S3 해당 사이트 방문해 읽어보면 좋을 것 같다.

설명 및 장단점 등은 다른 글에도 많으니 생략하고 사용방법 및 설정을 다루어 보자.

AWS S3

버킷 생성

S3를 사용하여 파일을 관리하기 위해선 먼저 버킷이라는 객체 관리 컨테이너를 만들어야한다. 사용할 버킷 이름을 작성하면 된다. 중복되지 않는 버킷 이름을 작성하고 언하는 리전을 선택(오른쪽 상단 닉네임 왼쪽에서 설정 가능) 버킷 만들기

이후 업로드한 사진을 확인하기 위해 퍼블릭엑세스 차단을 해제한다 객체 소유권 퍼블릭 엑세스 차단 설정

나머지는 그대로 설정해줬다 버킷 버전관리 + 기본 암호화 설정

IAM 사용자 생성

IAM사용자를 생성해 S3접근 권한을 부여해야한다. 엑세스키와 시크릿키를 사용하기 때문에 꼭 저장하고 이 값은 절대 외부에 노출되어선 안되니 주의해야한다. 필자는 git으로 관리할 때 .ignore파일에 application-secret파일에 DB정보나 엑세스/시크릿 키 등 주요 정보를 포함시켰다. IAM 사용자 직접 정책 연결을 선택하고 AmazonS3FullAccess를 포함시키고 사용자 생성 직접 정책 연결

다시 IAM으로 들어가 사용자를 클릭하고 엑세스 키 만들기를 누른다. 엑세스 키 만들기

보안 개선을 위한 사용사례 및 대안 고려인데 크게 차이가 없다 보안 개선을 위한 사용사례 및 대안 고려

이후 설명 태그 설정은 선택사항이기에 넘겼고 엑세스키를 만들었다.

엑세스키 .csv파일로 저장하도록 하는걸 추천!

권한 설정

버킷 정책을 편집을 클릭하고 본인의 버킷 ARN을 복사한 뒤 정책생성기를 누른다.

  • Select Type of Policy 에서 S3 Bucket - Policy를 선택
  • Principal에 * 입력
  • Actions에서 원하는 액션 체크 : 필자는 Get Object, Put Object, Delete Object 체크하였다
  • Amazon Resource Name (ARN) 에 위에서 복사한 ARN을 입력한 후 /* 입력
    • ex)arn:aws:s3:::버킷이름/* Add Statement 클릭

이후 Generate Policy하여 만들어진 JSON Document 복사후 변경에 붙여넣고 저장

이제 퍼블릭 설정이 완료되었다.

CORS 권한 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "x-amz-server-side-encryption",
            "x-amz-request-id",
            "x-amz-id-2"
        ],
        "MaxAgeSeconds": 3000
    }
]

붙여넣고 저장하면 된다.

SpringBoot 설정 및 연동

build.gradle / pom.xml

Spring Cloud AWS Starter 해당 mvn사이트로 가서 의존성을 가져와 원하는대로 추가해주면 된다

1
2
3
    //AWS
    // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-aws
    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.6.RELEASE'

pom.xml도 비슷하다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-aws</artifactId>
			<version>2.0.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-aws-context</artifactId>
			<version>1.2.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-aws-autoconfigure</artifactId>
			<version>1.2.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
		</dependency>
		<dependency>
		    <groupId>com.amazonaws</groupId>
		    <artifactId>aws-java-sdk-s3</artifactId>
		    <version>1.11.1001</version>
		</dependency>

application.properties / application.yml

application.properties

1
2
3
4
5
6
7
8
9
10
11
12
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
spring.config.import=application-secret.properties

# AWS credentials
cloud.aws.credentials.access-key=엑세스키
cloud.aws.credentials.secret-key=시크릿 엑세스 
cloud.aws.stack.auto=false

# AWS region and S3 bucket
cloud.aws.region.static=리전
cloud.aws.s3.bucket=버킷이름

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Multipart
servlet:
  multipart:
    enabled: true
    max-file-size: 10MB
    max-request-size: 10MB

# AWS
cloud:
  aws:
    credentials:
      accessKey: 엑세스키
      secretKey: 시크릿 엑세스 
    region:
      static: 리전
    stack:
      auto: false
    s3:
      bucket: 버킷이름

S3Config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ssafy.picple.AwsS3;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

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

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

    @Bean
    public AmazonS3Client amazonS3Client() {
        BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
        return (AmazonS3Client) AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
                .build();
    }
}

S3FileUploadService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.ssafy.picple.AwsS3;

import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import com.ssafy.picple.config.baseResponse.BaseException;
import com.ssafy.picple.config.baseResponse.BaseResponseStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.UUID;
import java.util.logging.Logger;

@Service
@Slf4j
@RequiredArgsConstructor
public class S3FileUploadService {
    private static final Logger logger = Logger.getLogger(S3FileUploadService.class.getName());
    private final AmazonS3 s3Client;

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

    private final String defaultUrl = "https://picple.s3.ap-northeast-2.amazonaws.com";

    public String uploadFile(MultipartFile file) throws IOException, BaseException {
        String originalFilename = file.getOriginalFilename();

        try {
            s3Client.putObject(bucketName, originalFilename, file.getInputStream(), getObjectMetadata(file));
            return defaultUrl + "/" + originalFilename;
        } catch (SdkClientException e) {
            throw new BaseException(BaseResponseStatus.FILE_UPLOAD_ERROR);
        }
    }

    // MultipartFile 사이즈랑 타입 명시용
    private ObjectMetadata getObjectMetadata(MultipartFile file) {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(file.getContentType());
        objectMetadata.setContentLength(file.getSize());
        return objectMetadata;
    }

    private String generateFileName(MultipartFile file) {
//        return UUID.randomUUID().toString() + "-" + file.getOriginalFilename(); // 중복안되게 랜덤하게 넣으려면 이렇게 그때그때 UUID붙여서
        return file.getOriginalFilename();
    }

    // 인코딩 필요하면 사용
    // 파일 이름을 UTF-8로 인코딩
    public static String encodeFileName(String fileName) {
        try {
            return URLEncoder.encode(fileName, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return fileName; // 인코딩 실패 시 원래 파일 이름 리턴
        }
    }

    public S3Object downloadFile(String fileName) throws BaseException {
        try {
            return s3Client.getObject(new GetObjectRequest(bucketName, fileName));
        } catch (SdkClientException e) {
            throw new BaseException(BaseResponseStatus.FILE_DOWNLOAD_ERROR);
        }
    }

    public void deleteFile(String file) throws BaseException {
        try {
            s3Client.deleteObject(new DeleteObjectRequest(bucketName, file));
        } catch (SdkClientException e) {
            throw new BaseException(BaseResponseStatus.FILE_DELETE_ERROR);
        }
    }

}

아래부턴 원하는대로 엔티티부터 코딩하면 된다. 다음은 내가 사용했던 컨트롤러 예시들

Controller

Maven, JDBC, MyBatis사용 프로젝트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
package com.fitdo.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
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;

import com.fitdo.model.dto.Follow;
import com.fitdo.model.dto.Post;
import com.fitdo.model.dto.User;
import com.fitdo.model.service.CommentService;
import com.fitdo.model.service.FollowService;
import com.fitdo.model.service.PostService;
import com.fitdo.model.service.S3FileUploadServiceImpl;
import com.fitdo.model.service.UserService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;

@RestController
@RequestMapping("/post")
@Tag(name = "PostRestController", description = "게시물 관리")
@CrossOrigin("*")
public class PostRestController {

	@Autowired
	private PostService ps;

	@Autowired
	private UserService us;

	@Autowired
	private S3FileUploadServiceImpl ss;

	@Autowired
	private FollowService fs;
	
	@Autowired
	private CommentService cs;

	// 게시물 전체 조회
	@GetMapping("/")
	@Operation(summary = "게시물 전체 조회")
	public ResponseEntity<List<Post>> getAllPosts() {
		List<Post> posts = ps.getPost();
		if (posts == null || posts.size() == 0) {
			return new ResponseEntity<>(HttpStatus.NO_CONTENT);
		}
		return new ResponseEntity<>(posts, HttpStatus.OK);
	}

	// 게시물 전체 조회 + 프사 + 댓글수
	@GetMapping("/withImg")
	@Operation(summary = "게시물 전체 조회 + 프사 + 댓글수")
	public ResponseEntity<List<Post>> getAllPostsWithProfileImg() {
		List<Post> posts = ps.getPostWithUserProfileImg();
		for (Post post : posts) {
			User user = us.searchById(post.getPostUserId()).get(0);
	        int commentsNum = cs.getCommentNum(post.getPostId());
			// 사용자 정보가 null이 아닌 경우에만 설정
			if (user != null) {
				post.setUser(user);
				post.setCommentNum(commentsNum);
			}
		} 
		System.out.println(posts);
		if (posts == null || posts.size() == 0) {
			return new ResponseEntity<>(HttpStatus.NO_CONTENT);
		}
		return new ResponseEntity<>(posts, HttpStatus.OK);
	}

	// 팔로우 하는 유저 게시물 전체 조회 + 본인포함
	@GetMapping("/followee/{userId}")
	@Operation(summary = "팔로우 하는 유저 게시물 전체 조회 + 본인포함")
	public ResponseEntity<List<Post>> getFolloweePosts(@PathVariable("userId") String userId) {

		List<Follow> followee = fs.selectFolloweeList(userId);
		System.out.println("내가 팔로우 하는 사람들 객체 : " + followee);

		List<String> followeeUserId = new LinkedList<>();
		for (Follow follow : followee) {
			followeeUserId.add(follow.getToUserId());
		}
		System.out.println("내가 팔로우 하는 사람들 아이디 목록 : " + followeeUserId);

		// 나도추가
		followeeUserId.add(userId);

		// 내가 볼 팔로우하는 사람들의 게시물 리스트
		List<Post> myFolloweePosts = new ArrayList<>();
		for (String followeeId : followeeUserId) {
			List<Post> userPosts = ps.selectPostsByUserId(followeeId);
			myFolloweePosts.addAll(userPosts);
		}

		List<Post> posts = myFolloweePosts;
		for (Post post : posts) {
			User user = us.searchById(post.getPostUserId()).get(0);
			// 사용자 정보가 null이 아닌 경우에만 설정
			if (user != null) {
				post.setUser(user);
			}
		}

		if (posts == null || posts.size() == 0) {
			return new ResponseEntity<>(HttpStatus.NO_CONTENT);
		}
		return new ResponseEntity<>(posts, HttpStatus.OK);
	}

	// 게시물 상세 조회
	@GetMapping("/{postId}")
	@Operation(summary = "게시물 상세 조회")
	public ResponseEntity<Post> getOnePost(@PathVariable int postId) {
		Post post = ps.selectOnePost(postId);
		if (post != null) {
			return new ResponseEntity<>(post, HttpStatus.OK);
		} else {
			return ResponseEntity.notFound().build();
		}
	}

	// 게시물 상세 조회
	@GetMapping("/getPostByUserId/{postUserId}")
	@Operation(summary = "게시물 유저아이디로 상세 조회 + 유저 정보")
	public ResponseEntity<Post> getOnePostByUserId(@PathVariable String postUserId) {
		Post post = ps.selectPostsByUserId(postUserId).get(0);
		User user = us.searchById(postUserId).get(0);
		if (user != null) {
			post.setUser(user);
		}
		if (post != null) {
			return new ResponseEntity<>(post, HttpStatus.OK);
		} else {
			return ResponseEntity.notFound().build();
		}
	}
	
	// 게시물들 유저아이디 일치하는 사람으로 조회
	@GetMapping("/getPostsListByUserId/{postUserId}")
	@Operation(summary = "게시물들 리스트 유저아이디로 상세 조회 - 게시물들 여러개 받는 거")
	public ResponseEntity<?> getPostsListByUserId(@PathVariable String postUserId) {
		List<Post> posts = ps.selectPostlistByUserId(postUserId);
		if (posts != null) {
			return new ResponseEntity<>(posts, HttpStatus.OK);
		} else {
			return ResponseEntity.notFound().build();
		}
	}
	
	// 게시물 등록
	@PostMapping("/addpost/{postUserId}")
	@Operation(summary = "게시물 등록")
	public ResponseEntity<?> createPost(@PathVariable("postUserId") String postUserId, @RequestBody Post post,
			HttpSession session, HttpServletRequest request, HttpServletResponse response) {
//		User user = (User) session.getAttribute("user");
//		System.out.println("user : " + user);
//        Cookie myCookie = new Cookie("myUserId : ", user.getUserid());
//        System.out.println(myCookie.getName());
//        System.out.println(myCookie.getValue());
//		Cookie[] cookies = request.getCookies();
//	    if (cookies != null) {
//	        for (Cookie cookie : cookies) {
//		        System.out.println("cookie.getName() : " + cookie.getName());
//		        System.out.println("cookie.getValue() : " + cookie.getValue());
//	        }
//	    }

		// Swagger통신시 세션저장이 안되는 문제점 해결 할 때 까지 테스트용 임시user "ssafy"생성

//		if (user == null) {
//			// 세션에 사용자 정보가 없을 때 임시로 사용자 객체 생성
//	        user = new User();
//	        user.setUserid("ssafy");
//	        // 임시로 생성한 사용자 객체를 세션에 저장
//	        session.setAttribute("user", user);
////			return new ResponseEntity<>("로그인이 필요합니다!", HttpStatus.UNAUTHORIZED);
//		}

//		post.setPostUserId(user.getUserid());

		// User객체까지 등록할 때 프론트단으로 넘겨줄거임
		User user = us.searchById(postUserId).get(0);
		post.setPostUserId(user.getUserid());
		post.setUser(user);
		System.out.println("postUserId : " + post.getPostUserId());
		System.out.println("user : " + user);
		System.out.println("userId : " + user.getUserid());
		System.out.println("userProfileImg : " + user.getProfileImg());

		int result = 0;
		try {
			result = ps.createPost(post, request);
		} catch (IllegalStateException e) {
			e.printStackTrace();
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 등록 실패!");
		} catch (IOException e) {
			e.printStackTrace();
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 등록 실패!");
		}
		if (result > 0) {
			return new ResponseEntity<>("게시물 등록 성공!", HttpStatus.CREATED);
		} else {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 등록 실패!");
		}
	}

	// 게시물 등록 및 이미지 업로드
	@PostMapping(value = "/addpostWithImg/{postUserId}", consumes = "multipart/form-data")
	@Operation(summary = "게시물 등록 및 이미지 업로드")
	public ResponseEntity<?> createPostWithImage(@PathVariable("postUserId") String postUserId,
			@RequestPart("post") @Valid Post post, @RequestPart("file") MultipartFile file, HttpSession session,
			HttpServletRequest request, HttpServletResponse response) throws IOException {

		int result = 0;
		try {
//	    	System.out.println("https://fit-do.s3.ap-southeast-2.amazonaws.com/"+file.getOriginalFilename());
//	        post.setPostImg("https://fit-do.s3.ap-southeast-2.amazonaws.com/"+file.getOriginalFilename());

			String fileUrl = ss.uploadFile(file);
			System.out.println("fileUrl : " + fileUrl);
			post.setPostImg(fileUrl);

			// User객체까지 등록할 때 프론트단으로 넘겨줄거임
			User user = us.searchById(postUserId).get(0);
			post.setPostUserId(user.getUserid());
			post.setUser(user);
			System.out.println("postUserId : " + post.getPostUserId());
			System.out.println("user : " + user);
			System.out.println("userId : " + user.getUserid());
			System.out.println("userProfileImg : " + user.getProfileImg());

			result = ps.createPost(post, request);
			if (result > 0) {
				return new ResponseEntity<>("게시물 등록 성공!", HttpStatus.CREATED);
			} else {
				return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 등록 실패!");
			}
		} catch (IllegalStateException e) {
			e.printStackTrace();
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 등록 실패!");
		} catch (IOException e) {
			e.printStackTrace();
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 등록 실패!");
		}
	}

	// 게시물 수정
	@PutMapping("/{postId}")
	@Operation(summary = "게시물 수정")
	public ResponseEntity<String> updatePost(@PathVariable int postId, @RequestBody Post post) {
		post.setPostId(postId);
		int result = ps.updatePost(post);
		if (result > 0) {
			return ResponseEntity.ok("게시물 수정 성공!");
		} else {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 수정 실패");
		}
	}

	// 게시물 사진 수정
	@PutMapping(value = "/{postid}/updateImage", consumes = "multipart/form-data")
	@Operation(summary = "게시물 사진 수정")
	public ResponseEntity<String> updatePostImg(@PathVariable int postid, @RequestPart MultipartFile file)
			throws IOException {
		try {
			String url = ss.uploadFile(file);
			System.out.println("게시물 사진 url : " + url);

			Post post = ps.selectOnePost(postid);

			if (post == null) {
				return new ResponseEntity<>("해당 게시물이 존재하지 않음", HttpStatus.NOT_FOUND);
			}
			int updateImg = ps.updatePostImageUrl(postid, url);
			System.out.println("updateImage : " + updateImg);

			return new ResponseEntity<>("게시물 사진 수정 성공!", HttpStatus.OK);
		} catch (IOException e) {
			return new ResponseEntity<>("사진 업로드 실패", HttpStatus.BAD_REQUEST);
		}
	}

	// 게시물 삭제
	@DeleteMapping("/{postId}")
	@Operation(summary = "게시물 삭제")
	public ResponseEntity<String> deletePost(@PathVariable int postId) {
		int result = ps.deletePost(postId);
		if (result > 0) {
			return ResponseEntity.ok("게시물 삭제!");
		} else {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 삭제 실패");
		}
	}
}

Gradle, JPA사용 프로젝트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.ssafy.picple.domain.photo.controller;

import com.ssafy.picple.AwsS3.S3FileUploadService;
import com.ssafy.picple.config.baseResponse.BaseResponseStatus;
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.RestController;

import com.ssafy.picple.config.baseResponse.BaseResponse;
import com.ssafy.picple.domain.photo.entity.Photo;
import com.ssafy.picple.domain.photo.service.PhotoService;

import lombok.RequiredArgsConstructor;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/photo")
@RequiredArgsConstructor
public class PhotoController {

	private final PhotoService photoService;
	private final S3FileUploadService s3FileUploadService;

	@PostMapping("")
	public BaseResponse<?> savePhoto(@RequestBody Photo photo, MultipartFile file) {
		try {
			String photoUrl = s3FileUploadService.uploadFile(file);

			Photo newPhoto = Photo.builder()
					.photoUrl(photoUrl)
					.isShared(false)
					.isDeleted(false)
					.build();
			return new BaseResponse<>(photoService.insertPhoto(newPhoto));
		} catch (Exception e) {
			return new BaseResponse<>(BaseResponseStatus.FILE_UPLOAD_ERROR);
		}
	}

}
This post is licensed under CC BY 4.0 by the author.