일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Anolog
- Spring Security
- spring
- real time web
- javascript
- flask
- hanghae99
- cookie
- session
- bean
- jQuery
- programmers
- 항해99
- Project
- SseEmitter
- Java
- google oauth
- web
- DI
- server send event
- Hibernate
- oauth
- Stream
- JPA
- python
- html
- WIL
- JWT
- 생명주기 콜백
- jenkins
- Today
- Total
끄적끄적 코딩일지
[Spring]이미지 월드컵 만들기 (6)- 이미지 업로드(2) 본문
저번글에서 대충 선택한 이미지 만큼 동적으로 해당 이미지와 이미지의 타이틀을 입력하는 항목을 추가하는것까지는 하였다.
2022.05.23 - [Project/[Spring] 이미지 월드컵 만들기] - [Spring]이미지 월드컵 만들기(5) - 이미지 업로드 설계(1)
그렇게 만들었으니 서버에 올라가는걸 테스트 할 차례
일단 그대로 서버에서 어떻게 값이 날라올지 확인해 보았다.
@PostMapping(value = "/world-cup-game")
public WorldCupGame makeWorldCupGame(WorldCupGame game){
System.out.println(game.toString());
return game;
}
결과는..
{name:null,createTime:null,updateTime:null,id:0,makeMember:null,description:ttt,games:[]}
?????
desription 외에 아무것도 변한게 없다.
참고로 createTime,updateTime,description은 Entity에 추가한것
createTime과 updateTime 은 따로 클레스를 만들어서 extends 하도록 했다.
package com.example.springTest.entity;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public abstract class TimeStamp {
@CreatedDate
private LocalDateTime createTime;
@LastModifiedDate
private LocalDateTime updateTime;
}
뭐 이미지 파일은 한번에 성공할꺼라고는 생각 안하고 있었고.... 그렇다고 해도 값 WorldCupGame의 title도 전달이 안되다니???
확인결과 이부분도 실수, WorldCupGame에 변수명을 name으로 해놓고서는 <input name="title">으로 해놓았던것.
이건 수정하고 페이지 검사를 통해 왜 값이 넘어가지 않았는지 페이지를 검사해보았다.
아....
동적으로 추가한 tag의 name값 수정이 않되었다.
예상되는 원인은 Javascript코드중 children(".img-title")을 못찾은것.
jquery는 편하긴 한데 가끔씩 아무런 오류도 내뱉지 않을때가 있다.
암튼 children(".card-img-top")은 잘 인식해서 src값을 변경한것을 보니까
div 하위 노드는 찾지 못한 모양.
보나마나 children 한번만 사용해서는 하위 노드의 하위노드를 찾지 못하는 것이겠지....
selectImageFile=(event)=>{
$('#img-list').empty();
let fileInput = $('#file-select')[0].files;
for(let i = 0; i < fileInput.length;i++){
let clone = $('#original').clone();
clone.children(".input-img").attr("src",URL.createObjectURL(fileInput[i]));
clone.children(".card-body").children(".img-file").files = fileInput[i];
clone.children(".card-body").children(".img-file").attr("name","games["+i+"].image");
clone.children(".card-body").children(".img-title").attr("name","games["+i+"].title");
clone.css("display","");
$('#img-list').append(clone);
}
}
그래서 children을 두번써서 실행 결과는....?
오류가 났다.
오류 내용을 보아하니 WorldCupItem의 image는 byte[] 인데 Multipartfile을 변환하지 못한다는 의미...
이걸 어떻게 할까 구글링을 열씸히 한 결과....
해결 방법 못찾음.... 키워드를 잘못 한걸지도 몰라도 죄다 한개의 input에서 여러개의 파일을 선택하는 multiple만 나온다.
또한 Multipart 자체를 byte[]를 포함한 Entity로 메핑하는 방법도 찾지를 못하였다.
그렇다고 form tag를 포기하고 ajax나 axios으로 구현하자니 작업이 많아져서 귀찮아진다.
하지만 개발자로써
확실히 구현할 수 있지만 오래걸리고 많은 오류를 거처야 하는 방법과 구현이 가능할지 모르지만 성공만 하면 간단하게 구현할 수 있는 길중 선택하라면 무조건 간단한 방법이라고 생각한다.
TMI : 게으른 개발자가 열심히 하는 개발자 보다 더 낳다라는 말이 있다. 열심히 하는 개발자가 코드 하나하나 다 칠때 게으른 개발자는 반복작업이 귀찮아서 모듈을 많들어 내고 수정을 적게 할수있는 구조로 개발을 한다. 때문에 기존 방식에 불평불만을 내놓는 게으른 개발자가 혁신을 일으킬 수 있다.
그래서 고민의 고민을 한 결과
그냥 한개의 input에서 다중 파일을 업로드하는 방식으로 구현하고 서버쪽에서 이를 합치기로 했다.
@RestController
public class GameController {
@Autowired
WorldCupGameService gameService;
@PostMapping(value = "/world-cup-game", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] makeWorldCupGame(WorldCupGame game,@RequestPart List<MultipartFile> files){
game.build(files);
return game.getGames().get(0).getImage();
}
}
Controller에서는 이미지 byte값이 제대로 들어갔는지 확인하기 위해 잠시 이미지를 return하는것으로 수정하였다.
아래는 build method 추가
package com.example.springTest.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.web.multipart.MultipartFile;
import javax.persistence.*;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Entity
@ToString
public class WorldCupGame extends TimeStamp {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private long id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY,targetEntity = Member.class,cascade = CascadeType.ALL)
@JoinColumn
private Member makeMember;
@Column
private int playCount = 0;
@Column
private String description;
@OneToMany(fetch = FetchType.LAZY,targetEntity=WorldCupItem.class,cascade = CascadeType.ALL)
private List<WorldCupItem> games = new ArrayList<WorldCupItem>();
public void addItem(WorldCupItem item){
this.games.add(item);
}
public void removeItem(WorldCupItem item){
this.games.remove(item);
}
public void build(List<MultipartFile> files){
for (int i = 0; i < files.size(); i++) {
try {
games.get(i).setImage(files.get(i).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="//unpkg.com/bootstrap@4/dist/css/bootstrap.min.css">
<script src='//unpkg.com/jquery@3/dist/jquery.min.js'></script>
<script src='//unpkg.com/bootstrap@4/dist/js/bootstrap.min.js'></script>
<script type="text/javascript" src="/js/worldcup_add.js"></script>
</head>
<body>
<form id="worldcup-form" class="p-3" action="/world-cup-game" method="post" enctype="multipart/form-data">
<label for="worldcup-title" class="form-label">월드컵 Title</label>
<input id="worldcup-title" name="name" class="form-control">
<label for="worldcup-dcs" class="form-label">월드컵 설명</label>
<input id="worldcup-dcs" class="form-control" name="description">
<input id="file-select" name="files" type="file" class="btn btn-outline-primary" value="이미지 파일 선택" accept=".png,.jpg" onchange="selectImageFile()" multiple>
<div class="container">
<div class="card col-md-3" id="original" style="display: none">
<img class="input-img card-img-top" src="">
<div class="card-body"> <!-- <input type="file"> 삭제! -->
<input class="form-control img-title" type="text" placeholder="이미지 설명..." name="">
</div>
</div>
<div class="row" id="img-list">
</div>
<input type="submit" class="btn btn-outline-success" value="월드컵 만들기">
</div>
</form>
</body>
</html>
selectImageFile=(event)=>{
$('#img-list').empty();
let fileInput = $('#file-select')[0].files;
for(let i = 0; i < fileInput.length;i++){
let clone = $('#original').clone();
clone.children(".input-img").attr("src",URL.createObjectURL(fileInput[i]));
// clone.children(".card-body").children(".img-file").files = fileInput[i];
// clone.children(".card-body").children(".img-file").attr("name","files["+i+"].file");
clone.children(".card-body").children(".img-title").attr("name","games["+i+"].title");
clone.css("display","");
$('#img-list').append(clone);
}
}
이전에 이미지 개수만큼 <input type="file">을 추가해서
clone.children(".card-body").children(".img-file").files = fileInput[i]; 으로 하나하나 files 값을 세팅하는것이 아니라
이미지 선택하는 input에서 모든 값을 List<MultipartFile>으로 보내고 그것을 WorldCupGame에 셋팅하는 방식으로 바꿨다.
일종의 편법인 샘
과연 그 결과는???
엌ㅋㅋㅋㅋㅋㅋㅋ 이게 진짜로 되네 ㅋㅋㅋㅋㅋㅋㅋㅋ
이 편법이 진짜로 먹힐줄이야 ㅋㅋㅋㅋㅋㅋ
System.out.println찍어서 title과 이미지가 제대로 mapping이 되는지도 확인하였다.
물론 제대로된 방법은 아니라서 나중에 다시 알아봐야 하겠지만 말이다.
다시 코드를 수정해서 Database에 잘 수정이 되는지 확인해보자
오... 아주 잘 들어가는데?
그럼 여기까지 한 김에 Database에서 이미지를 조회해서 리턴하는 Api를 만들어 보자
@RestController
public class GameItemAPI {
@Autowired
private WorldCupGameItemService service;
@GetMapping(value = "/image/{id}",produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] getImage(@PathVariable("id") String gameItemId){
System.out.println(gameItemId);
return service.getImageById(Long.parseLong(gameItemId));
}
}
@Service
@Getter
public class WorldCupGameItemService {
@Autowired
private WorldCupItemRepo repo;
public byte[] getImageById(long id){
return repo.findById(id).get().getImage();
}
}
아주 만족스럽게 잘 나온다
데이터 생성 기능은 얼추 되었으니
다음글에서는 메인페이지 및 게임 페이지를 만들어 보겠다.
'Project > [Spring] 이미지 월드컵 만들기' 카테고리의 다른 글
[Spring] 이미지월드컵 만들기 (마지막) - 메인화면 월드컵 진행 및 결과 화면 (0) | 2022.06.02 |
---|---|
[Spring]이미지 월드컵 만들기(5) - 이미지 업로드(1) (0) | 2022.05.23 |
[Spring]이미지 월드컵 만들기(4) - 기능 설계하기 (1) | 2022.05.18 |
[Spring]이미지 월드컵 만들기(3) - DB Entity 설계하기 (0) | 2022.05.18 |
[Spring]이미지 월드컵 만들기(2) - Database 연결 (0) | 2022.05.18 |