- 게시글 업로드 시 해시태그를 추가할 수 있도록 구현해보았습니다.
- 추가된 해시태그는 이후 게시글 하단에 표시되게 되며 이후에 해당 태그를 이용하여 태그 검색 기능도 추가해볼 예정입니다.
📝 upload.jsp
- 아래 코드는 이미지 파일 업로드를 위한 폼 태그를 구현한 부분으로, 기존에 사용하던 코드입니다.
<!--사진업로드 Form-->
<form class="upload-form" action="/image" method="POST" enctype="multipart/form-data">
<input type="file" name="file" onchange="imageChoose(this)"/>
<div class="upload-img">
<img src="/images/person.jpeg" alt="" id="imageUploadPreview" />
</div>
<!--사진설명 + 업로드버튼-->
<div class="upload-form-detail">
<input type="text" placeholder="사진을 설명해보세요!" name="caption">
<!-- 해시 태그 -->
<div class="upload-form-hashtag" align="center">
<input type="text" placeholder="해시태그를 추가해보세요!" id="hashtag" name="hashtag">
<button type="button" class="cta blue" onclick="addHashtag()">추가</button>
</div>
<!-- 추가한 해시 태그 리스트 -->
<div class="hashtagList" id="hashtagList">
</div>
<!-- 해시 태그 end -->
<button class="cta blue">업로드</button>
</div>
<!--사진설명end-->
</form>
<!--사진업로드 Form-->
- 우선 위 코드를 아래와 같이 수정해주었습니다.
- 이는 이후 추가될 해시태그 목록들을 FormData 객체에 저장하여 ajax를 통해 서버로 전송하기 위함입니다.
<!--사진업로드 Form-->
<form id="uploadForm" class="upload-form">
<input type="file" name="file" onchange="imageChoose(this)"/>
<div class="upload-img">
<img src="/images/person.jpeg" alt="" id="imageUploadPreview" />
</div>
<!--사진설명 + 업로드버튼-->
<div class="upload-form-detail">
<input type="text" placeholder="사진을 설명해보세요!" name="caption">
<!-- 해시 태그 -->
<div class="upload-form-hashtag" align="center">
<input type="text" placeholder="해시태그를 추가해보세요!" id="hashtag" name="hashtag">
<button type="button" class="cta blue" onclick="addHashtag()">추가</button>
</div>
<!-- 추가한 해시 태그 리스트 -->
<div class="hashtagList" id="hashtagList">
</div>
<!-- 해시 태그 end -->
<button type="button" id="uploadBtn" class="cta blue">업로드</button>
</div>
<!--사진설명end-->
</form>
<!--사진업로드 Form-->
📝 upload.js
- ajax 요청과 해시태그 추가, 삭제 로직은 아래와 같이 구현하였습니다.
$(function() {
$("#uploadBtn").on("click", function() {
upload();
});
});
function upload() {
let form = $("#uploadForm")[0];
let formData = new FormData(form);
//추가된 해시태그를 모두 FormData 객체에 저장하기
let hashtagList = document.getElementsByClassName("hashtag-value");
for(let i = 0; i < hashtagList.length; i++) {
formData.append("hashtagList[" + i + "]", hashtagList[i].innerText);
}
$.ajax({
type: "POST",
url: `/api/upload`,
data: formData,
contentType: false, //false 로 선언 시 content-type 헤더가 multipart/form-data로 전송되게 함
processData: false //false로 선언 시 formData를 string으로 변환하지 않음
}).done(resp => {
console.log(resp);
location.href=`http://localhost:8080/user/${resp.data}`
}).fail(error => {
console.log(error);
});
}
// 해시 태그 추가, 삭제 로직
function addHashtag() {
let hashtag = $("#hashtag").val();
let item = getHashtagItem(hashtag);
$("#hashtagList").append(item);
}
function removeHashtag(hashtag) {
document.getElementById(hashtag).remove();
}
function getHashtagItem(hashtag) {
let item =
`<div class="hashtag" id="${hashtag}">
<span class="hashtag-value">#${hashtag}</span>
<button type="button" onclick="removeHashtag('${hashtag}')">×</button>
</div>`
return item;
}
📝 ImageApiController
- 이후 위 요청을 받는 컨트롤러 함수를 아래와 같이 구현해주었습니다.
package com.cos.photogram.web.api;
...
@RequiredArgsConstructor
@RestController
public class ImageApiController {
private final ImageService imageService;
/* 이미지 업로드 */
@PostMapping("/api/upload")
public ResponseEntity<?> imageUpload(ImageUploadDto imageUploadDto,
@AuthenticationPrincipal PrincipalDetails principalDetails) {
if(imageUploadDto.getFile().isEmpty()) {
throw new CustomValidationException("이미지가 첨부되지 않았습니다.", null);
}
imageService.upload(imageUploadDto, principalDetails);
return new ResponseEntity<>(new CMRespDto<>(1, "이미지 업로드 성공", principalDetails.getUser().getId()), HttpStatus.OK);
}
...
}
- FormData 객체를 전달받기 위한 DTO는 아래와 같이 구현되어 있습니다.
package com.cos.photogram.web.dto.image;
...
@Data
public class ImageUploadDto {
private MultipartFile file;
private String caption;
private List<String> hashtagList;
public Image toEntity(User user, String post_image_url) {
String hashtag = "";
for(String str : hashtagList) {
hashtag += str + ",";
}
return Image.builder()
.user(user)
.post_image_url(post_image_url)
.caption(caption)
.hashtag(hashtag)
.build();
}
}
📝 ImageService
- 서비스 로직은 아래와 같습니다.
package com.cos.photogram.service;
...
@RequiredArgsConstructor
@Service
public class ImageService {
private final ImageRepository imageRepository;
/* 이미지 업로드 */
@Transactional
public void upload(ImageUploadDto imageUploadDto, PrincipalDetails principalDetails) {
UUID uuid = UUID.randomUUID();
String imageFileName = uuid + "_" + imageUploadDto.getFile().getOriginalFilename();
Path imageFilePath = Paths.get(uploadFolder + imageFileName);
try {
Files.write(imageFilePath, imageUploadDto.getFile().getBytes());
} catch(Exception e) {
e.printStackTrace();
}
Image image = imageUploadDto.toEntity(principalDetails.getUser(), imageFileName);
imageRepository.save(image);
}
...
}
📝 Image
- Image 개체에는 DB에 해시태그를 저장하기 위한 문자열 형 속성과 이후 이를 파싱하여 View에서 사용되어질 리스트형 필드를 추가해주었습니다.
package com.cos.photogram.domain.image;
...
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Image {
...
private String hashtag;
@Transient
private List<String> hashtagList;
...
}
- 문자열 -> 배열 파싱은 아래와 같이 구현하였습니다.
/* 스토리 페이지 */
@Transactional(readOnly = true)
public Page<Image> story(Long principalId, Pageable pageable) {
Page<Image> images = imageRepository.story(principalId, pageable);
images.forEach((image) -> {
...
//해시태그 문자열 -> 해시태그 리스트
image.setHashtagList(Arrays.asList(image.getHashtag().split(",")));
});
return images;
}
📝 story.js
- 이후 View 페이지에서는 아래와 같이 해시태그 리스트를 이용하여 해시태그 목록을 출력해줍니다.
image.hashtagList.forEach((hashtag) => {
item += `<div class="sl__item__contents__tag">${hashtag}</div>`
});
'🚗 Backend Toy Project > Photogram' 카테고리의 다른 글
[Photogram] 연관 검색어 (0) | 2023.03.05 |
---|---|
[Photogram] 실시간 알림 (0) | 2023.03.02 |
[Photogram] 로그인 실패 처리 (0) | 2022.12.25 |
[Photogram] 검색 기능 구현 (0) | 2022.12.22 |
[Photogram] OAuth2 페이스북 로그인 (0) | 2022.07.19 |