
참고자료
[Spring Boot] AWS S3를 이용한 파일 업로드
AWS S3 란? AWS Simple Storage Service의 줄임말로 파일 서버의 역할을 하는 서비스 프로젝트 개발 중 파일을 저장하고 불러오는 작업이 필요한 경우에 프로젝트 내부 폴더에 저장할 수 있지만, AWS S3를
chb2005.tistory.com
멀티파트(Multipart)란? Multipart 전송과 MultipartResolver 를 통한 요청 처리
1. 멀티파트(Multipart)란?멀티파트는 클라이언트와 서버 간에 전송되는 HTTP 요청 또는 응답에서 여러 종류의 데이터를 동시에 전송하기 위해 사용되는 방식이다.일반적으로 파일 업로드와 관련된
sharonprogress.tistory.com
image_picker_web | Flutter package
Flutter Web Plugin to pick Images (as Widget, File or Uint8List) and Videos (as File or Uint8List)
pub.dev
과정
목표 기능
로그인을 하고, Profile에 가면 다음과 같이 조회가 된다.

여기서 프로필 사진을 클릭해서 바꿀 수 있는 기능을 지원하려고 한다.
서버에 이미지를 보내고 저장하는 기능이 필요하다.
이미지를 DB에 저장하면 비용이 아까우니, 다른 곳에 저장하고 Reference를 할 예정이다.
AWS의 S3 또는, Cloudflare의 R2(S3와 호환된다.)를 주로 사용하는 것 같다.
이번엔 S3 Bucket으로 구현하기로 정했다.


Local_image 선택
Image_picker 라이브러리를 사용했다.
flutter에서 local_image를 선택할 수 있는 기능이 구현되어 있다.

이미지 데이터를 얻고, 이를 Post 요청으로 서버에 보내야 한다.
이미지를 성공적으로 전송 했다면, flutter 화면에도 업데이트가 되어야 한다.
Get 요청을 해도 되지만, local에 있는 데이터를 그대로 사용하는게 경제적일 것 같아 그렇게 작성했다.
Future<Uint8List?> pickUpImage(BuildContext context) async {
Uint8List? bytesFromPicker = await ImagePickerWeb.getImageAsBytes();
return bytesFromPicker;
}
Future<void> uploadImageToServer(
BuildContext context, Uint8List imageByte) async {
final Dio dio = Dio();
dio.options.extra['withCredentials'] = true;
String base64Image = base64Encode(imageByte);
FormData formData = FormData.fromMap({
"file": await MultipartFile.fromBytes(imageByte, filename: 'image.jpg')
});
try {
Response response = await dio.post(
UserApi.myinfo,
data: formData,
);
if (response.statusCode == 200) {
print("Image uploaded successfully");
setState(
() {
_imageUrl = 'data:image/jpeg;base64,${base64Image}';
},
);
} else {
print("Failed to upload image");
}
} catch (e) {
print('Error uploading image: $e');
}
}
업로드 Api 구축하다가...
참조 글을 참고하면, Bucket 세팅을 쉽게 할 수 있다.
그리고 Bucket을 S3 api를 활용해서 하는 기능도 단순하게 구현할 수 있다.


요청이 성공적으로 들어갔고, Bucket에도 잘 들어갔음을 확인할 수 있다.
하지만 다시 api를 호출하면, 프로필 사진이 원래대로 돌아온다..!
@authenticationprincipal에 대하여
나는 아래의 코드를 이용하여 User를 손쉽게 관리하고 있었다.
그리고 이 코드가 현재 버그의 주범이다.
@Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@AuthenticationPrincipal(expression = "#this == 'anonymouseUser' ? null : User")
public @interface ReqUser {
}
@AuthenticationPrincipal을 이해하기 위해 아래의 이미지를 찾아냈다.

Spring Security에서 인증이 진행된다.
인증이 완료되면, SecuirtyContext에 Authentication이 저장된다.
@AuthenticationPrincipal 이 어노테이션은,
SecurityContext가 들고있는 Auth의 Principal(보안 주체)의 정보를 가져오는 어노테이션이다.
그렇다면, 처음에 로그인했을 때 생성이 됐을 것이다.
그리고 이 값을 계속 주입할 것이다.
즉, 값이 갱신되더라도, Session에 있는 값은 갱신이 되지 않기 때문에
User의 ProfileImage를 변경하더라도, DB엔 적용이 되나 API에 적용이 되지 않았던 것이다.
User를 가볍게 가져가다
Session을 새로 생성 해 버리니, 기존에 인증한 사용자가 튕겨버린다.
애초에 Session 값을 건드리는 자체가 보안적으로 옳지 않은 선택이라 한다.
그래서 User에 ImageURL을 빼고 테이블을 분리 했다.
기존 Service는 User객체에서 그대로 값을 다 뽑아냈다.
그래서 User객체에서는 id와 nickName만 적고,
Service에서 이 id를 활용해서 다른 테이블의 값을 꺼내게 변경 했다.
구현 끝


프로필 사진이 잘 변경 된다!
DB에도 잘 저장이 되었고, Bucket에도 저장이 잘 되었다 야호!
피드백
시간을 많이 낭비하긴 했지만 많이 배웠다.
숙련도가 부족해서 시행착오에 시간을 많이 쓴거 같다.
반복적인 훈련으로 기본적인 기능을 기계적으로 생성할 정도로 훈련하고,
고급 테크닉을 연마하는 것이 맞을 것 같다.
'프로젝트 > Beatn-beat [비튼 - 비트]' 카테고리의 다른 글
15. Local, Dev, Prod 설정 분리하기 (0) | 2024.02.24 |
---|---|
14. EC2 + Route53 배포 (0) | 2024.02.17 |
12. Nginx로 같은 host에서 운영하기 (0) | 2024.02.14 |
11. 인증과의 전쟁 (0) | 2024.02.12 |
10. Spring-boot와 OAuth2.0 (0) | 2024.02.11 |

참고자료
[Spring Boot] AWS S3를 이용한 파일 업로드
AWS S3 란? AWS Simple Storage Service의 줄임말로 파일 서버의 역할을 하는 서비스 프로젝트 개발 중 파일을 저장하고 불러오는 작업이 필요한 경우에 프로젝트 내부 폴더에 저장할 수 있지만, AWS S3를
chb2005.tistory.com
멀티파트(Multipart)란? Multipart 전송과 MultipartResolver 를 통한 요청 처리
1. 멀티파트(Multipart)란?멀티파트는 클라이언트와 서버 간에 전송되는 HTTP 요청 또는 응답에서 여러 종류의 데이터를 동시에 전송하기 위해 사용되는 방식이다.일반적으로 파일 업로드와 관련된
sharonprogress.tistory.com
image_picker_web | Flutter package
Flutter Web Plugin to pick Images (as Widget, File or Uint8List) and Videos (as File or Uint8List)
pub.dev
과정
목표 기능
로그인을 하고, Profile에 가면 다음과 같이 조회가 된다.

여기서 프로필 사진을 클릭해서 바꿀 수 있는 기능을 지원하려고 한다.
서버에 이미지를 보내고 저장하는 기능이 필요하다.
이미지를 DB에 저장하면 비용이 아까우니, 다른 곳에 저장하고 Reference를 할 예정이다.
AWS의 S3 또는, Cloudflare의 R2(S3와 호환된다.)를 주로 사용하는 것 같다.
이번엔 S3 Bucket으로 구현하기로 정했다.


Local_image 선택
Image_picker 라이브러리를 사용했다.
flutter에서 local_image를 선택할 수 있는 기능이 구현되어 있다.

이미지 데이터를 얻고, 이를 Post 요청으로 서버에 보내야 한다.
이미지를 성공적으로 전송 했다면, flutter 화면에도 업데이트가 되어야 한다.
Get 요청을 해도 되지만, local에 있는 데이터를 그대로 사용하는게 경제적일 것 같아 그렇게 작성했다.
Future<Uint8List?> pickUpImage(BuildContext context) async {
Uint8List? bytesFromPicker = await ImagePickerWeb.getImageAsBytes();
return bytesFromPicker;
}
Future<void> uploadImageToServer(
BuildContext context, Uint8List imageByte) async {
final Dio dio = Dio();
dio.options.extra['withCredentials'] = true;
String base64Image = base64Encode(imageByte);
FormData formData = FormData.fromMap({
"file": await MultipartFile.fromBytes(imageByte, filename: 'image.jpg')
});
try {
Response response = await dio.post(
UserApi.myinfo,
data: formData,
);
if (response.statusCode == 200) {
print("Image uploaded successfully");
setState(
() {
_imageUrl = 'data:image/jpeg;base64,${base64Image}';
},
);
} else {
print("Failed to upload image");
}
} catch (e) {
print('Error uploading image: $e');
}
}
업로드 Api 구축하다가...
참조 글을 참고하면, Bucket 세팅을 쉽게 할 수 있다.
그리고 Bucket을 S3 api를 활용해서 하는 기능도 단순하게 구현할 수 있다.


요청이 성공적으로 들어갔고, Bucket에도 잘 들어갔음을 확인할 수 있다.
하지만 다시 api를 호출하면, 프로필 사진이 원래대로 돌아온다..!
@authenticationprincipal에 대하여
나는 아래의 코드를 이용하여 User를 손쉽게 관리하고 있었다.
그리고 이 코드가 현재 버그의 주범이다.
@Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@AuthenticationPrincipal(expression = "#this == 'anonymouseUser' ? null : User")
public @interface ReqUser {
}
@AuthenticationPrincipal을 이해하기 위해 아래의 이미지를 찾아냈다.

Spring Security에서 인증이 진행된다.
인증이 완료되면, SecuirtyContext에 Authentication이 저장된다.
@AuthenticationPrincipal 이 어노테이션은,
SecurityContext가 들고있는 Auth의 Principal(보안 주체)의 정보를 가져오는 어노테이션이다.
그렇다면, 처음에 로그인했을 때 생성이 됐을 것이다.
그리고 이 값을 계속 주입할 것이다.
즉, 값이 갱신되더라도, Session에 있는 값은 갱신이 되지 않기 때문에
User의 ProfileImage를 변경하더라도, DB엔 적용이 되나 API에 적용이 되지 않았던 것이다.
User를 가볍게 가져가다
Session을 새로 생성 해 버리니, 기존에 인증한 사용자가 튕겨버린다.
애초에 Session 값을 건드리는 자체가 보안적으로 옳지 않은 선택이라 한다.
그래서 User에 ImageURL을 빼고 테이블을 분리 했다.
기존 Service는 User객체에서 그대로 값을 다 뽑아냈다.
그래서 User객체에서는 id와 nickName만 적고,
Service에서 이 id를 활용해서 다른 테이블의 값을 꺼내게 변경 했다.
구현 끝


프로필 사진이 잘 변경 된다!
DB에도 잘 저장이 되었고, Bucket에도 저장이 잘 되었다 야호!
피드백
시간을 많이 낭비하긴 했지만 많이 배웠다.
숙련도가 부족해서 시행착오에 시간을 많이 쓴거 같다.
반복적인 훈련으로 기본적인 기능을 기계적으로 생성할 정도로 훈련하고,
고급 테크닉을 연마하는 것이 맞을 것 같다.
'프로젝트 > Beatn-beat [비튼 - 비트]' 카테고리의 다른 글
15. Local, Dev, Prod 설정 분리하기 (0) | 2024.02.24 |
---|---|
14. EC2 + Route53 배포 (0) | 2024.02.17 |
12. Nginx로 같은 host에서 운영하기 (0) | 2024.02.14 |
11. 인증과의 전쟁 (0) | 2024.02.12 |
10. Spring-boot와 OAuth2.0 (0) | 2024.02.11 |