121일차
1. 현재회차 수정기능
1.1 DTO 수정
Bean Validation을 할때 문제점은 저장시와 수정시에 받는 값이 다를 수 있다는 것이다.
우리 프로젝트의 경우 입력할때는 round값을 직접 입력하도록 만들어놓았는데 수정에선 현재 회차를 그대로 사용해야하기 때문에 round값을 입력하지 않는다.
그래서 같은 DTO를 사용하게 되면 null값이 들어오면서 문제가 발생하게 된다.
이 문제를 해결하기 위해서 dto를 분리해주어야한다.
1.1.1 ExhibitionInfo
기존의 ExhibitionInfo는 말그대로 dto의 역할을 하게 된다.
@Getter
@Setter
@ToString
public class ExhibitionInfo {
//@NotNull(message = "공백은 허용되지 않습니다.")
private Integer round;
// @NotBlank(message = "공백은 허용되지 않습니다.")
private String location;
//@NotBlank(message = "공백은 허용되지 않습니다.")
// @Pattern(regexp = "\\w+@\\w+\\.\\w+", message = "이메일 형식이 올바르지 않습니다.")
private String managerEmail;
//@NotBlank(message = "공백은 허용되지 않습니다.")
private String organizer;
//@NotBlank(message = "공백은 허용되지 않습니다.")
private String startDate;
//@NotBlank(message = "공백은 허용되지 않습니다.")
private String endDate;
private String etc;
private String bus;
private String subway;
//@NotBlank(message = "공백은 허용되지 않습니다.")
private String title;
private List<String> fileName;
public ExhibitionInfo() {
}
public ExhibitionInfo(Integer round, String location, String managerEmail, String organizer, String startDate, String endDate, String etc, String bus, String subway, String title) {
this.round = round;
this.location = location;
this.managerEmail = managerEmail;
this.organizer = organizer;
this.startDate = startDate;
this.endDate = endDate;
this.etc = etc;
this.bus = bus;
this.subway = subway;
this.title = title;
}
}1.1.2 ExhibitionInfoSaveForm
저장하는 form은 round값을 받고 null값을 처리한다.
public class ExhibitionInfoSaveForm {
@NotNull(message = "회차의 공백이 허용되지 않습니다.")
private Integer round;
@NotBlank(message = "주소의 공백이 허용되지 않습니다.")
private String location;
@NotBlank(message = "담당자 이메일의 공백이 허용되지 않습니다.")
@Pattern(regexp = "\\w+@\\w+\\.\\w+", message = "이메일 형식이 올바르지 않습니다.")
private String managerEmail;
@NotBlank(message = "개최자의 공백이 허용되지 않습니다.")
private String organizer;
@NotBlank(message = "개최일의 공백이 허용되지 않습니다.")
private String startDate;
@NotBlank(message = "폐막일의 공백이 허용되지 않습니다.")
private String endDate;
private String etc;
private String subway;
@NotBlank(message = "박람회 타이틀의 공백이 허용되지 않습니다.")
private String title;
private List<String> fileName;1.1.3 ExhibitionInfoUpdateForm
updateform도 따로 만들어주었다.
public class ExhibitionInfoUpdateForm {
private Integer round;
@NotBlank(message = "공백은 허용되지 않습니다.")
private String location;
@NotBlank(message = "공백은 허용되지 않습니다.")
@Pattern(regexp = "\\w+@\\w+\\.\\w+", message = "이메일 형식이 올바르지 않습니다.")
private String managerEmail;
@NotBlank(message = "공백은 허용되지 않습니다.")
private String organizer;
@NotBlank(message = "공백은 허용되지 않습니다.")
private String startDate;
@NotBlank(message = "공백은 허용되지 않습니다.")
private String endDate;
private String etc;
private String bus;
private String subway;
@NotBlank(message = "공백은 허용되지 않습니다.")
private String title;
private List<String> fileName;
}1.2 컨트롤러
reg컨트롤러는 살펴보았으니 updateform을 보도록 하자.
예외가 발생하면 bindingResult에 담겨서 오류가 발생하면 해당 로직으로 가게된다.
만약 오류가 발생하지 않는다면 검증이 통과된 상태이다.
ExhibitionInfoUpdateForm에 담겨온 값을 ExhibitionInfo객체 생성자를 통해서 값을 담아주고 사용하면된다.
@PostMapping("/update")
public String updateProc(@Valid @ModelAttribute("exhibitionInfo") ExhibitionInfoUpdateForm form,
BindingResult bindingResult,
@RequestParam(value = "removeFiles", required = false) List<String> removeFileNames,
@RequestParam(value = "files", required = false) MultipartFile[] files,
RedirectAttributes rttr) {
// 검증에 실패 시 bindingResult 객체에 에러 정보 담음
if (bindingResult.hasErrors()) {
return "admin/round/update";
}
// 검증에 성공한 경우 처리 로직 실행
// 여기 까지 오면 exhibitionInfo 객체는 검증이 통과된 상태이다.
Boolean ok = false;
ExhibitionInfo exhibitionInfo = new ExhibitionInfo(
form.getRound(),
form.getLocation(),
form.getManagerEmail(),
form.getOrganizer(),
form.getStartDate(),
form.getEndDate(),
form.getEtc(),
form.getBus(),
form.getSubway(),
form.getTitle()
);
// 등록하기
try {
ok = exhibitionInfoService.update(exhibitionInfo, removeFileNames, files);
} catch (IOException e) {
throw new RuntimeException(e);
}
// 필요한 작업을 수행한 후 다른 페이지로 리다이렉트
if (ok) {
rttr.addFlashAttribute("message", "수정이 완료되었습니다.");
return "redirect:/admin/round";
} else {
rttr.addFlashAttribute("message", "수정이 실패했습니다.");
return "redirect:/admin/round/update";
}
}ExhibitionInfoUpdateForm은 웹 계층에서만 사용되는 객체로 검증을 할때만 사용하게 된다.
비즈니스 로직이나 데이터 저장에 사용되기에는 적합하지 않을 수 있다.
이런 경우에는 서비스 레이어로 전달할 때 별도의 DTO 객체를 생성하고
ExhibitionInfoUpdateForm의 데이터를 해당 DTO에 매핑하여 전달하는 것이 일반적이라고 한다.
그래서 이런 방식을 이용해서 ExhibitionInfo에 담아 사용하게 되었다.
1.3 서비스
서비스는 비슷하다. 파일을 삭제하고 등록하는 과정을 거치게 된다.
// 회차 수정
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean update(ExhibitionInfo exhibitionInfo,
List<String> removeFileNames,
MultipartFile[] files) throws IOException {
Integer round = exhibitionInfo.getRound();
// 파일 삭제
if (removeFileNames != null && !removeFileNames.isEmpty()) {
for (String fileName : removeFileNames) {
removeFromS3(round, fileName);
// FileName 테이블의 데이터 삭제
exhibitionInfoMapper.deleteFileNameByRoundAndFileName(round, fileName);
}
}
if (files != null) {
fileToS3(exhibitionInfo, files);
}
// 박람회 정보 update
Integer cnt = exhibitionInfoMapper.update(exhibitionInfo);
return cnt == 1;
}1.4 view
위 코드와 별개로 update를 하려면 update페이지에 일단 값을 보여준 후 그 값을 들고가서 해야한다.
스프링의 form:form태그를 사용하면 아주 좋은 기능을 해준다. model에 담긴 객체를 가지고 input박스에 name, value를 알아서 넣어준다.
타임리프에서는 이런것이 쉽게 가능했는데 jsp에선 이게 불가능하다고 생각되었으나 이런 방법이 있었다.
<div class="container-lg mt-3">
<div class="row justify-content-center">
<div class="col-12 col-md-8 col-lg-6">
<h3>회차 수정 하기</h3>
<form:form method="post" modelAttribute="exhibitionInfo" enctype="multipart/form-data">
<div class="mb-3">
<label for="round" class="form-label">회차</label>
<form:input path="round" type="number" id="round" class="form-control" readonly="true"/>
</div>
<div class="mb-3">
<label for="title" class="form-label">박람회 타이틀</label>
<form:input path="title" value="${exhibitionInfo.title}" id="title" type="text"
class="form-control"/>
<form:errors path="title" cssClass="field-error"/>
</div>
<div class="mb-3">
<label for="email" class="form-label">담당자 이메일</label>
<form:input path="managerEmail" id="email" type="email" class="form-control"/>
<form:errors path="managerEmail" cssClass="field-error"/>
</div>
<div class="mb-3">
<label for="organizer" class="form-label">개최자</label>
<form:input path="organizer" type="text" class="form-control"/>
<form:errors path="organizer" cssClass="field-error"/>
</div>
<div class="mb-3">
<label for="start-date" class="form-label">개최일</label>
<form:input path="startDate" id="start-date" type="date" class="form-control"/>
<form:errors path="startDate" cssClass="field-error"/>
</div>
<div class="mb-3">
<label for="end-date" class="form-label">폐막일</label>
<form:input path="endDate" id="end-date" type="date" class="form-control"/>
<form:errors path="endDate" cssClass="field-error"/>
</div>
<div class="mb-3">
<label for="input-address" class="form-label">주소</label>
<div class="input-group">
<form:input path="location" id="input-address" type="text" class="form-control mb-1"
readonly="true" placeholder="도로명 주소"/>
<button class="btn btn-outline-secondary" type="button" id="search-address-btn">주소검색</button>
</div>
<form:errors path="location" cssClass="field-error"/>
</div>
<div class="mb-3">
<label for="bus" class="form-label">버스</label>
<form:input path="bus" id="bus" type="text" class="form-control"/>
<form:errors path="bus" cssClass="field-error"/>
<div class="form-text">, 없이 띄어쓰기로 구분해서 넣어주세요</div>
</div>
<div class="mb-3">
<label for="subway" class="form-label">지하철</label>
<form:input path="subway" id="subway" type="text" class="form-control"/>
<form:errors path="subway" cssClass="field-error"/>
<div class="form-text">, 없이 띄어쓰기로 구분해서 넣어주세요</div>
</div>
<div class="mb-3">
<label for="etc" class="form-label">기타</label>
<form:textarea path="etc" type="text" class="form-control" rows="5"/>
<form:errors path="etc" cssClass="field-error"/>
</div>
<div id="file-name" class="mb-3">
<label class="form-label">첨부 파일 목록</label>
<c:forEach items="${exhibitionInfo.fileName}" var="fileName">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="removeCheckBox"
name="removeFiles"
value="${fileName}">
<label class="form-check-label" for="removeCheckBox">
<i class="fa-solid fa-trash-can" style="color: red;"></i>
</label>
</div>
<a class="form-control" href="${bucketUrl}/exhibitionInfo/${exhibitionInfo.round}/${fileName}"
download="${fileName}">${fileName}</a>
</c:forEach>
</div>
<div class="mb-3">
<label for="form-file" class="form-label">첨부 파일</label>
<input class="form-control" name="files" type="file" id="form-file" multiple>
<div class="form-text">총 10MB, 하나의 파일을 1MB를 초과할 수 없습니다.</div>
<div class="form-text">로고는 logo.png로 설명은 info.png로 등록해주세요</div>
</div>
<div>
<button class="btn btn-primary" type="submit">등록</button>
<a href="/admin/round" class="btn btn-primary">현재 회차 정보 보기</a>
</div>
</form:form>
</div>
</div>
</div>2023.07.31
스프링의 form:form태그를 처음 사용하면서 좋은 기능이 있다는 것을 알게 되었다.
실무에선는 form태그 자체를 잘 사용하지 않는다고는 하지만 쉽고 좋은 기능이다.
restful api에서도 이렇게 검증을 할 수는 있지만 문제점은 해당페이지로 돌아가는게 어려운 점이 있었다.
다른 공부를 통해서 자주사용하는 방식인 restful에서도 적용을 해보고 싶다.
'국비 > Project-3 채용박람회' 카테고리의 다른 글
| 2023.07.24 123일차 Team Project - 2 멘토님과의 만남(긴급 수정 발생) (0) | 2023.07.31 |
|---|---|
| 2023.07.21 122일차 Team Project - 2 패키지리팩토링(소통의 부재) (0) | 2023.07.31 |
| 2023.07.19 120일차 Team Project - 2 채용박람회 비활성화아이디 찾기 (0) | 2023.07.31 |
| 2023.07.18 119일차 Team Project - 2 채용박람회 Validation (0) | 2023.07.30 |
| 2023.07.17 118일차 Team Project - 2 채용박람회 (0) | 2023.07.30 |