[세미프로젝트] 9월 19일 구현 - 레시피 작성창, 레시피 등록, 레시피 상세 조회 완료, 레시피 수정 진행중

hanahana·2022년 9월 19일
0
post-thumbnail
  • 부트 스트랩을 이용하면 반응형이 자연스럽게 될줄 알았는데 생각처럼 반응형이 만들어지지 않아 무척 속상하다
  • 반응형까지 다시 작업할 시간은 없을 것 같다.
  • 그래도 오늘은 레시피 작성폼을 거의 완성했다. 등록/취소 버튼만 손을 보고 나중에 로그인 창이 들어왔을때 사용자 이메일만 세션으로 받아주면 된다.
  • 상세 레시피 출력까지 성공했고 수정창도 원하는 대로 정상 입력되었다.
  • 오늘 가장 어려웠던건 생각대로 움직이지 않는 front단과 레시피 수정창을 입력할때 forEach문을 써 넣으면 내가 원하는 만큼 목록이 반환되지 않아 폼이 망가질것이기때문에 list를 forEach문을 쓰지 않고 비어있는것은 비어있는대로 들어온 값만 넣는것이었다.
    • 이건 성공했으니 여기에 코드를 공유하며 자세히 설명할까 한다.

레시피 등록 폼 jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>

<head>

<title>레시피 등록</title>
<link
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
	rel="stylesheet"
	integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
	crossorigin="anonymous">
<script
	src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
	integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
	crossorigin="anonymous"></script>

<style>
body {
	margin: 0 auto;
}
</style>

</head>

<body>

	<form action="/recipe/regist.do" method="post"
		enctype="multipart/form-data">
		<div class="container-lg align-items-center">
			<div class="container-lg col-sm-12 row align-items-center p-0">

				<!-- 여기는 대표 이미지 박스 -->
				<div class="col-md-5 col-sm-12 container-lg align-items-center">

					<div class="row col-md-4 m-2 col-sm-12  align-items-center">

						<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
							fill="currentColor" class="bi bi-x-circle-fill float-end"
							onclick="mainPicDel()">
						<path
								d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
					  </svg>
						<label class="input-file-button float-start" for="input-file">
							<img id="output" class="mx-auto " width="400px" height="270px"
							style="background-color: gray; border-radius: 10px;">
						</label> <input type="file" id="input-file" style="display: none"
							accept="image/jpeg, image/png, image/jpg" name="mainPicture"
							class="isFile" onchange="loadFile(event)" required="required" />

					</div>

				</div>

				<div class="col-md-7 container-lg col-sm-12">

					<div class="form-floating col-sm-12 ">
						<input type="text" class="form-control" id="" name="recipeName">
						<label for="floatingInput">레시피 제목</label>
					</div>
					<br>
					<div class="form-floating col-sm-12 ">
						<input type="text" class="form-control" id="" name="recipeVideo" onblur="isYoutube()">
						<label for="floatingInput">유튜브 링크</label>
					</div>

					<div class="mt-5 col-12">
						<label for="validationCustom04" class="form-label"><h5>카테고리</h5></label>
						<select class="form-select" id="" name="recipeCategory" required>
							<option selected disabled value="">카테고리 선택</option>
							<option value="mael">식사</option>
							<option value="relish">술안주</option>
							<option value="dessert">간식</option>
							<option value="drink">술/음료</option>
						</select>
						<div class="invalid-feedback">카테고리를 선택하세요</div>
						<br>

						<div class="form-floating align-items-center">
							<input type="text" class="form-control mb-2" id=""
								name="recipeTime"> <label for="floatingInput">소요시간</label>
						</div>

						<div class="col-md-12 col-sm-12">
							<h5>해시태그</h5>
							<div class="overflow-hidden">
								<label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="jmt"
									value="true"> JMT</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="healthy"
									value="true"> 건강한</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="goodSpicy"
									value="true"> 맛있게 매운</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="soSpicy"
									value="true"> 아주매운</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="sweet"
									value="true"> 달콤한</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="easy"
									value="true"> 간편한</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="full"
									value="true"> 든든한</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="party"
									value="true"> 파티용</label>
							</div>
						</div>

					</div>

				</div>

				<br>

				<div class=" row m-0 mt-3 mb-3  p-0 ">
					<div class="form-floating">
						<input type="text" class="form-control" id="" maxlength="30"
							name="recipeInfo" required="required"> <label
							for="floatingInput"> 간단한소개 (30자 미만)</label>
					</div>
				</div>

				<!-- 재료 입력-->
				<div class="container align-items-center">

					<div class=" col-md-12 m-0 p-0">

						<div class="row col-md-6 col-12 float-start p-3 me-1">
							<div class="form-floating col-md-6">
								<input type="text" class="form-control" id="" maxlength="10"
									name="material"> <label for="floatingInput">재료명</label>
							</div>
							<div class="form-floating col-md-6">
								<input type="text" class="form-control" id="" maxlength="10"
									name="amount"> <label for="floatingInput">재료수량</label>
							</div>
						</div>

						<div class="row col-md-6 col-12 float-start p-3 ms-1">
							<div class="form-floating col-md-6">
								<input type="text" class="form-control" id="" maxlength="10"
									name="material"> <label for="floatingInput">재료명</label>
							</div>

							<div class="form-floating col-md-6">
								<input type="text" class="form-control" id="" maxlength="10"
									name="amount"> <label for="floatingInput">재료수량</label>
							</div>
						</div>

						<div class=" col-md-12">

							<div class="row col-md-6 col-12 float-start p-3 me-1">
								<div class="form-floating col-md-6">
									<input type="text" class="form-control" id="" maxlength="10"
										name="material"> <label for="floatingInput">재료명</label>
								</div>
								<div class="form-floating col-md-6">
									<input type="text" class="form-control" id="" maxlength="10"
										name="amount"> <label for="floatingInput">재료수량</label>
								</div>
							</div>

							<div class="row col-md-6 col-12 float-start p-3 ms-1">
								<div class="form-floating col-md-6">
									<input type="text" class="form-control" id="" maxlength="10"
										name="material"> <label for="floatingInput">재료명</label>
								</div>

								<div class="form-floating col-md-6">
									<input type="text" class="form-control" id="" maxlength="10"
										name="amount"> <label for="floatingInput">재료수량</label>
								</div>
							</div>

							<div class=" col-md-12 border-bottom-1">

								<div class="row col-md-6 col-12 float-start p-3 me-1">
									<div class="form-floating col-md-6">
										<input type="text" class="form-control" id="" maxlength="10"
											name="material"> <label for="floatingInput">재료명</label>
									</div>
									<div class="form-floating col-md-6">
										<input type="text" class="form-control" id="" maxlength="10"
											name="amount"> <label for="floatingInput">재료수량</label>
									</div>
								</div>

								<div class="row col-md-6 col-12 float-start p-3 ms-1" >
									<div class="form-floating col-md-6">
										<input type="text" class="form-control" id="" maxlength="10"
											name="material"> <label for="floatingInput">재료명</label>
									</div>

									<div class="form-floating col-md-6">
										<input type="text" class="form-control" id="" maxlength="10"
											name="amount"> <label for="floatingInput">재료수량</label>

									</div>

								</div>

							</div>

						</div>

					</div>

				</div>
			</div>

			<div class="recipeDetail container row">
				<!-- 여기서부터 레시피 설명-->
				<div>
					<hr>
					<div class="row container p-3 float-start col-md-6 p-2">

						<div class="form-floating col-md-12 p-0 pt-3">
							<textarea class="form-control" name="recipeDescription"  placeholder=""
								id="floatingTextarea2" style="height: 250px"></textarea>
							<label for="floatingTextarea2" class="mt-3">설명을 입력하세요</label>
						</div>

						<!-- 					설명에 쉼표를 넣었을때 배열로 만들지 않기위한 더미 value -->
						<input type="text" name="recipeDescription" value="ab22bb"
							style="display: none">

					</div>

					<!-- 이미지 미리보기 이미지 파일 -->
					<div class="row col-md-6 float-start p-2">
						<div
							class="col-md-5 col-sm-12 container-lg align-items-center m-0">
							<div class="row col-md-4 col-sm-12  align-items-center">
								<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
									fill="currentColor" class="bi bi-x-circle-fill float-end"
									onclick="PicDel(this)">
							<path
										d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
						  </svg>

								<label class="input-file-button float-start"> <img
									class="detailImg mx-auto" width="400px" height="270px"
									style="background-color: gray; border-radius: 10px;"> <input
									type="file" name="recipePicture" class="isFile"
									style="display: none" accept="image/jpeg, image/png, image/jpg"
									onchange="imgView(this);">
								</label>

							</div>

						</div>
					</div>
				</div>
				<!-- 레시피 한블럭 설명종료 -->
				<!-- 여기서부터 레시피 설명-->
				<div>

					<div class="row container p-3 float-start col-md-6 p-2">

						<div class="form-floating col-md-12 p-0 pt-3">
							<textarea class="form-control" placeholder=""
								id="floatingTextarea2" name="recipeDescription"  style="height: 250px"></textarea>
							<label for="floatingTextarea2" class="mt-3">설명을 입력하세요</label>
						</div>

						<!-- 					설명에 쉼표를 넣었을때 배열로 만들지 않기위한 더미 value -->
						<input type="text" name="recipeDescription" value="ab22bb"
							style="display: none">

					</div>

					<!-- 이미지 미리보기 이미지 파일 -->
					<div class="row col-md-6 float-start p-2">
						<div
							class="col-md-5 container-lg align-items-center m-0">
							<div class="row col-md-4  align-items-center">
								<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
									fill="currentColor" class="bi bi-x-circle-fill float-end"
									onclick="PicDel(this)">
							<path
										d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
						  </svg>

								<label class="input-file-button float-start"> <img
									class="detailImg mx-auto" width="400px" height="270px"
									style="background-color: gray; border-radius: 10px;"> <input
									type="file" name="recipePicture" class="isFile"
									style="display: none" accept="image/jpeg, image/png, image/jpg"
									onchange="imgView(this);">
								</label>

							</div>

						</div>
					</div>
				</div>
				<!-- 레시피 한블럭 설명종료 -->
				<!-- 여기서부터 레시피 설명-->
				<div id="REdetail" class="REdetailC">

					<div class="row container p-3 float-start col-md-6 p-2">

						<div class="form-floating col-md-12 p-0 pt-3">
							<textarea class="form-control" placeholder=""
								id="floatingTextarea2" name="recipeDescription" style="height: 250px"></textarea>
							<label for="floatingTextarea2" class="mt-3">설명을 입력하세요</label>
						</div>

						<!-- 					설명에 쉼표를 넣었을때 배열로 만들지 않기위한 더미 value -->
						<input type="text" name="recipeDescription" value="ab22bb"
							style="display: none">

					</div>

					<!-- 이미지 미리보기 이미지 파일 -->
					<div class="row col-md-6 float-start p-2" >
						<div
							class="col-md-5 container-lg align-items-center m-0">
							<div class="row col-md-4  align-items-center">
								<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
									fill="currentColor" class="bi bi-x-circle-fill float-end"
									onclick="PicDel(this)">
							<path
										d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
						  </svg>

								<label class="input-file-button float-start">
								 <img class="detailImg mx-auto" width="400px" height="270px"
									style="background-color: gray; border-radius: 10px;">
								 <input type="file" name="recipePicture" class="isFile"
									style="display: none" accept="image/jpeg, image/png, image/jpg"
									onchange="imgView(this);">
								</label>

							</div>

						</div>
					</div>
				</div>
				<!-- 레시피 한블럭 설명종료 -->
				<span id="addplace"></span>
			</div>
			<!-- 레시피 종료 -->
			
			
			<svg class="m-2" xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" class="bi bi-plus-circle-fill" viewBox="0 0 16 16" onclick="addDe()">
  <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3v-3z"/>
</svg>

<svg  class= "m-2"xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" class="bi bi-dash-circle-fill" viewBox="0 0 16 16" onclick="removeDe()">
  <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM4.5 7.5a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1h-7z"/>
</svg>

			<div class="col-md-12">
				<input type="submit" value="등록" onclick="checkMainPic();">
			</div>
		</div>
	</form>

	</div>

	<script>
	
	//유튜브 링크 유효성체크//
	
 	var youUrl = /(http:|https:)?(\/\/)?(www\.)?(youtube.com|youtu.be)\/(watch|embed)?(\?v=|\/)?(\S+)?/g;
	function isYoutube(){
		var youtube = document.querySelector('[name="recipeVideo"]');
		if(!youUrl.test(youtube.value)){
			  alert("유튜브 주소는 embed이 포함된 고유주소로 입력해주세요")
			  youtube.value="";
			
		} 
		
	}
	
	
	///설명 추가/삭제////
	var detailForm =document.querySelector('#REdetail');
	var addplace =document.querySelector('#addplace');
	var count = 3;
	function addDe(){
		if(count<=10){
		addplace.appendChild(detailForm.cloneNode(true));
		 document.querySelectorAll('.REdetailC')[document.querySelectorAll('.REdetailC').length-1].childNodes[1].childNodes[1].childNodes[1].value="";
		 document.querySelectorAll('.REdetailC')[document.querySelectorAll('.REdetailC').length-1].childNodes[5].childNodes[1].childNodes[1].childNodes[3].childNodes[3].value="";
		
		count++;
		}
		
		
	}
	
	function removeDe(){
	var detailFormClass = document.querySelectorAll('.REdetailC');
		if(count>3){
			 detailFormClass[ detailFormClass.length-1].remove();
		count--;
		}
		
		
	}

	
	
		function loadFile(event) {
			var output = document.getElementById('output');
			output.src = URL.createObjectURL(event.target.files[0]);
			output.onload = function() {
				URL.revokeObjectURL(output.src) // free memory
			};
			imgCheck()
		};

		///// 이미지파일 체크 시작 ////
		var imgFile = document.querySelectorAll('.isFile');
		var fileForm = /(.*?)\.(jpg|jpeg|png|gif|bmp)$/i;
		var fileSize;
		function checkMainPic() {
			if (imgFile[0].value == "") {
				alert("대표사진은 필수입니다!");

			}
		};

		function imgCheck() {
			for (var i = 0; i < imgFile.length; i++) {
				if (imgFile[i].value != "") {

					if (!imgFile[i].value.match(fileForm)) {
						alert("이미지 파일만 업로드 가능");
						imgFile[i].value = "";

					}
				}
			}

		};

		/////이미지 확장자 체크 종료///

		function imgView(obj) {

			var imgid = obj.previousElementSibling;

			imgid.src = URL.createObjectURL(event.target.files[0]);
			imgid.onload = function() {
				URL.revokeObjectURL(imgid.src) // free memory

			}
			imgCheck()

		};

		function mainPicDel() {
			imgFile[0].value = "";
			output.removeAttribute('src');
		}

		function PicDel(obj) {	
			var imgfiles = obj.nextElementSibling.childNodes[1];
			var imgfilesinput = obj.nextElementSibling.childNodes[3];
			imgfilesinput.value = "";
			imgfiles.removeAttribute('src');

		}
	</script>

</body>
</html>
  • 등록에 관해서는 어제 거의 완성했기때문에 backend부분은 거의 손대지 않았다.
  • 오늘 추가된 부분은 폼을 깔끔하게 다듬기
  • 모든 이미지 추가창에 이미지 미리보기 가능하게 스크립트 넣기
    				<!-- 여기서부터 레시피 설명-->
    				<div id="REdetail" class="REdetailC">
    
    					<div class="row container p-3 float-start col-md-6 p-2">
    
    						<div class="form-floating col-md-12 p-0 pt-3">
    							<textarea class="form-control" placeholder=""
    								id="floatingTextarea2" name="recipeDescription" style="height: 250px"></textarea>
    							<label for="floatingTextarea2" class="mt-3">설명을 입력하세요</label>
    						</div>
    
    						<!-- 					설명에 쉼표를 넣었을때 배열로 만들지 않기위한 더미 value -->
    						<input type="text" name="recipeDescription" value="ab22bb"
    							style="display: none">
    
    					</div>
    
    					<!-- 이미지 미리보기 이미지 파일 -->
    					<div class="row col-md-6 float-start p-2" >
    						<div
    							class="col-md-5 container-lg align-items-center m-0">
    							<div class="row col-md-4  align-items-center">
    								<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
    									fill="currentColor" class="bi bi-x-circle-fill float-end"
    									onclick="PicDel(this)">
    							<path
    										d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
    						  </svg>
    
    								<label class="input-file-button float-start">
    								 <img class="detailImg mx-auto" width="400px" height="270px"
    									style="background-color: gray; border-radius: 10px;">
    								 <input type="file" name="recipePicture" class="isFile"
    									style="display: none" accept="image/jpeg, image/png, image/jpg"
    									onchange="imgView(this);">
    								</label>
    
    							</div>
    
    						</div>
    					</div>
    				</div>
    				<!-- 레시피 한블럭 설명종료 -->
    • 이미지 폼과 크기를 맞추기위해 text를 textarea로 수정하였다
    • 수정하면서 name을 다시 넣는것을 잊어버려 테이블에 제대로 값이 들어가지 않는 문제가 생겼지만 name을 잘 넣으니 해결되었다.
    • 이미지 미리보기에 관해서는
      	function imgView(obj) {
      
      			var imgid = obj.previousElementSibling;
      
      			imgid.src = URL.createObjectURL(event.target.files[0]);
      			imgid.onload = function() {
      				URL.revokeObjectURL(imgid.src) // free memory
      
      			}
      			imgCheck()
      
      		};
      • 이 코드를 사용했다. 이미지 폼은 10개까지 늘어가나며 다 같은 함수를 사용하기에 this를 이용해여 코드가 사용된 옆에 있는 img 태그를 선택하였고
      • 선택한 img태그에 src를 넣어 미리보기가 가능하게 하였다.
  • 유튜브 주소 유효성 검사하기
     	var youUrl = /(http:|https:)?(\/\/)?(www\.)?(youtube.com|youtu.be)\/(watch|embed)?(\?v=|\/)?(\S+)?/g;
    	function isYoutube(){
    		var youtube = document.querySelector('[name="recipeVideo"]');
    		if(!youUrl.test(youtube.value)){
    			  alert("유튜브 주소는 embed이 포함된 고유주소로 입력해주세요")
    			  youtube.value="";
    			
    		} 
    		
    	}
    • 유튜브의 단축 주소는 동영상의 ifram을 출력할수 없어서 embed가 포함된 주소를 받기 위한 유효성 체크이다
  • 설명form을 10개까지 추가 삭제하기 이다
    	///설명 추가/삭제////
    	var detailForm =document.querySelector('#REdetail');
    	var addplace =document.querySelector('#addplace');
    	var count = 3;
    	function addDe(){
    		if(count<=10){
    		addplace.appendChild(detailForm.cloneNode(true));
    		 document.querySelectorAll('.REdetailC')[document.querySelectorAll('.REdetailC').length-1].childNodes[1].childNodes[1].childNodes[1].value="";
    		 document.querySelectorAll('.REdetailC')[document.querySelectorAll('.REdetailC').length-1].childNodes[5].childNodes[1].childNodes[1].childNodes[3].childNodes[3].value="";
    		
    		count++;
    		}
    		
    		
    	}
    	function removeDe(){
    	var detailFormClass = document.querySelectorAll('.REdetailC');
    		if(count>3){
    			 detailFormClass[ detailFormClass.length-1].remove();
    		count--;
    		}
    		
    		
    	}
    
    • count를 만들어 count가 10개가 될때까지만 설명창을 추가할수있으며
    • 3개가 될때까지 삭제할수 있도록 했다.
    • 설명창을 추가할때 3번째 설명창을 복사하여 추가했기떄문에 3번째 설명창에 내용이 있으면 그 내용까지 복사하는 오류가 있었다.
      • 좀 귀찮았지만 마지막으로 만들어진 창을 기준으로 value가 들어가는 속성을 찾아 모든 value를 공백으로 만들어 해결했다.

수정창 만들기

  • 시간이 별로 안남았지만 수정창까지는 만들수 있을것 같아 만들어보았다

레시피 수정 jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>

<head>

<title>레시피 등록</title>
<link
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
	rel="stylesheet"
	integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
	crossorigin="anonymous">
<script
	src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
	integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
	crossorigin="anonymous"></script>

<style>
body {
	margin: 0 auto;
}
</style>

</head>

<body>

	<form action="/recipe/regist.do" method="post"
		enctype="multipart/form-data">
		<div class="container-lg align-items-center">
			<div class="container-lg col-sm-12 row align-items-center p-0">

				<!-- 여기는 대표 이미지 박스 -->
				<div class="col-md-5 col-sm-12 container-lg align-items-center">

					<div class="row col-md-4 m-2 col-sm-12  align-items-center">

						<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
							fill="currentColor" class="bi bi-x-circle-fill float-end"
							onclick="mainPicDel()">
						<path
								d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
					  </svg>
						<label class="input-file-button float-start" for="input-file">
							<img id="output" class="mx-auto " width="400px" height="270px"
							src="/resources/recipeImg/${recipe.mainPicRename }"
							style="background-color: gray; border-radius: 10px;">
						</label> <input type="file" id="input-file" style="display: none"
							accept="image/jpeg, image/png, image/jpg" name="mainPicture"
							class="isFile" onchange="loadFile(event)" required="required" />

					</div>

				</div>

				<div class="col-md-7 container-lg col-sm-12">

					<div class="form-floating col-sm-12 ">
						<input type="text" class="form-control" id="" name="recipeName"
							value="${recipe.recipeName }"> <label for="floatingInput">레시피
							제목</label>
					</div>
					<br>
					<div class="form-floating col-sm-12 ">
						<input type="text" class="form-control" id="" name="recipeVideo"
							value="${recipe.recipeVideo }" onblur="isYoutube()"> <label
							for="floatingInput">유튜브 링크</label>
					</div>

					<div class="mt-5 col-12">
						<label for="validationCustom04" class="form-label"><h5>카테고리</h5></label>
						<select class="form-select" id="" name="recipeCategory" required>
							<option selected disabled value="">카테고리 선택</option>
							<option value="mael"
								<c:if test="${recipeCategory == mael}"> selected</c:if>>식사</option>
							<option value="relish"
								<c:if test="${recipeCategory == relish}"> selected</c:if>>술안주</option>
							<option value="dessert"
								<c:if test="${recipeCategory == dessert}"> selected</c:if>>간식</option>
							<option value="drink"
								<c:if test="${recipeCategory == drink}"> selected</c:if>>술/음료</option>
						</select>
						<div class="invalid-feedback">카테고리를 선택하세요</div>
						<br>

						<div class="form-floating align-items-center">
							<input type="text" class="form-control mb-2" id=""
								value="${recipe.recipeTime }" name="recipeTime"> <label
								for="floatingInput">소요시간</label>
						</div>

						<div class="col-md-12 col-sm-12">
							<h5>해시태그</h5>
							<div class="overflow-hidden">
								<label class="w-25 float-start"> <input
									class="form-check-input" type="checkbox" name="jmt"
									<c:if test="${rTag.jmt==true }">checked</c:if> value="true">
									JMT
								</label> <label class="w-25 float-start"> <input
									class="form-check-input" type="checkbox" name="healthy"
									<c:if test="${rTag.healthy==true }">checked</c:if> value="true">
									건강한
								</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="goodSpicy"
									<c:if test="${rTag.goodSpicy==true }">checked</c:if>
									value="true"> 맛있게 매운</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="soSpicy"
									<c:if test="${rTag.soSpicy==true }">checked</c:if> value="true">
									아주매운</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="sweet"
									<c:if test="${rTag.sweet==true }">checked</c:if> value="true">
									달콤한</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="easy"
									<c:if test="${rTag.easy==true }">checked</c:if> value="true">
									간편한</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="full"
									<c:if test="${rTag.full==true }">checked</c:if> value="true">
									든든한</label> <label class="w-25 float-start"><input
									class="form-check-input" type="checkbox" name="party"
									<c:if test="${rTag.party==true }">checked</c:if> value="true">
									파티용</label>
							</div>
						</div>

					</div>

				</div>

				<br>

				<div class=" row m-0 mt-3 mb-3  p-0 ">
					<div class="form-floating">
						<input type="text" class="form-control" id="" maxlength="30"
							name="recipeInfo"  value ="${recipe.recipeInfo}"required="required"> <label
							for="floatingInput"> 간단한소개 (30자 미만)</label>
					</div>
				</div>

				<!-- 재료 입력-->
				<div class="container align-items-center">

					<div class=" col-md-12 m-0 p-0">

						<div class="row col-md-6 col-12 float-start p-3 me-1">
							<div class="form-floating col-md-6">
								<input type="text" class="form-control" id="" maxlength="10"
								<c:if test="${rmListSize >=1  }"> value="${rmList.get(0).material }"</c:if>	name="material"> <label for="floatingInput">재료명</label>
							</div>
							<div class="form-floating col-md-6">
								<input type="text" class="form-control" id="" maxlength="10"
								<c:if  test="${rmListSize >=1  }"> value="${rmList.get(0).material }"</c:if>	name="amount"> <label for="floatingInput">재료수량</label>
							</div>
						</div>

						<div class="row col-md-6 col-12 float-start p-3 ms-1">
							<div class="form-floating col-md-6">
								<input type="text" class="form-control" id="" maxlength="10"
								<c:if  test="${rmListSize >=2  }"> value="${rmList.get(1).material }"</c:if>	name="material"> <label for="floatingInput">재료명</label>
							</div>

							<div class="form-floating col-md-6">
								<input type="text" class="form-control" id="" maxlength="10"
								<c:if  test="${rmListSize >=2  }"> value="${rmList.get(1).amount }"</c:if>	name="amount"> <label for="floatingInput">재료수량</label>
							</div>
						</div>

						<div class=" col-md-12">

							<div class="row col-md-6 col-12 float-start p-3 me-1">
								<div class="form-floating col-md-6">
									<input type="text" class="form-control" id="" maxlength="10"
									<c:if  test="${rmListSize >=3  }"> value="${rmList.get(2).material }"</c:if>	name="material"> <label for="floatingInput">재료명</label>
								</div>
								<div class="form-floating col-md-6">
									<input type="text" class="form-control" id="" maxlength="10"
									<c:if  test="${rmListSize >=3  }"> value="${rmList.get(2).amount }"</c:if>	name="amount"> <label for="floatingInput">재료수량</label>
								</div>
							</div>

							<div class="row col-md-6 col-12 float-start p-3 ms-1">
								<div class="form-floating col-md-6">
									<input type="text" class="form-control" id="" maxlength="10"
									<c:if  test="${rmListSize >=4  }"> value="${rmList.get(3).material }"</c:if>		name="material"> <label for="floatingInput">재료명</label>
								</div>

								<div class="form-floating col-md-6">
									<input type="text" class="form-control" id="" maxlength="10"
									<c:if  test="${rmListSize >=4  }"> value="${rmList.get(3).amount }"</c:if>		name="amount"> <label for="floatingInput">재료수량</label>
								</div>
							</div>

							<div class=" col-md-12 border-bottom-1">

								<div class="row col-md-6 col-12 float-start p-3 me-1">
									<div class="form-floating col-md-6">
										<input type="text" class="form-control" id="" maxlength="10"
										<c:if  test="${rmListSize >=5  }"> value="${rmList.get(4).material }"</c:if>	name="material"> <label for="floatingInput">재료명</label>
									</div>
									<div class="form-floating col-md-6">
										<input type="text" class="form-control" id="" maxlength="10"
										<c:if  test="${rmListSize >=5  }"> value="${rmList.get(4).amount }"</c:if>	name="amount"> <label for="floatingInput">재료수량</label>
									</div>
								</div>

								<div class="row col-md-6 col-12 float-start p-3 ms-1">
									<div class="form-floating col-md-6">
										<input type="text" class="form-control" id="" maxlength="10"
										<c:if  test="${rmListSize >=6  }"> value="${rmList.get(5).amount }"</c:if>		name="material"> <label for="floatingInput">재료명</label>
									</div>

									<div class="form-floating col-md-6">
										<input type="text" class="form-control" id="" maxlength="10"
										<c:if  test="${rmListSize >=6  }"> value="${rmList.get(5).amount }"</c:if>		name="amount"> <label for="floatingInput">재료수량</label>

									</div>

								</div>

							</div>

						</div>

					</div>

				</div>
			</div>

			<div class="recipeDetail container row">
				<!-- 여기서부터 레시피 설명-->
				<!-- 레시피step forEach문시작 -->
				<c:forEach items="${rsList }" var="rStep">
					<div>
						<hr>
						<div class="row container p-3 float-start col-md-6 p-2">

							<div class="form-floating col-md-12 p-0 pt-3">
								<textarea class="form-control" name="recipeDescription"
									placeholder="" id="floatingTextarea2" style="height: 250px">${rStep.recipeDescription }</textarea>
								<label for="floatingTextarea2" class="mt-3">설명을 입력하세요</label>
							</div>

							<!-- 					설명에 쉼표를 넣었을때 배열로 만들지 않기위한 더미 value -->
							<input type="text" name="recipeDescription" value="ab22bb"
								style="display: none">

						</div>

						<!-- 이미지 미리보기 이미지 파일 -->
						<div class="row col-md-6 float-start p-2">
							<div
								class="col-md-5 col-sm-12 container-lg align-items-center m-0">
								<div class="row col-md-4 col-sm-12  align-items-center">
									<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
										fill="currentColor" class="bi bi-x-circle-fill float-end"
										onclick="PicDel(this)">
							<path
											d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
						  </svg>

									<label class="input-file-button float-start"> <img
										class="detailImg mx-auto" width="400px" height="270px"
										<c:if test="${rStep.recipePicRename  ne null }">
									src="/resources/recipeImg/${rStep.recipePicRename }"</c:if>
										style="background-color: gray; border-radius: 10px;"> <input
										type="file" name="recipePicture" class="isFile"
										style="display: none"
										accept="image/jpeg, image/png, image/jpg"
										onchange="imgView(this);">
									</label>

								</div>

							</div>
						</div>
					</div>
				</c:forEach>
				<!-- 레시피step foreach문 종료 -->
				<!-- 레시피 한블럭 설명종료 -->

			</div>
			<!-- 레시피 종료 -->

			<!-- 설명추가 -->
			<svg class="m-2" xmlns="http://www.w3.org/2000/svg" width="30"
				height="30" fill="currentColor" class="bi bi-plus-circle-fill"
				viewBox="0 0 16 16" onclick="addDe()">
  <path
					d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3v-3z" />
</svg>
			<!--  설명 삭제 -->
			<svg class="m-2" xmlns="http://www.w3.org/2000/svg" width="30"
				height="30" fill="currentColor" class="bi bi-dash-circle-fill"
				viewBox="0 0 16 16" onclick="removeDe()">
  <path
					d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM4.5 7.5a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1h-7z" />
</svg>

			<div class="col-md-12">
				<input type="submit" value="수정" onclick="checkMainPic();">
			</div>
		</div>
	</form>

	</div>

	<script>
		//유튜브 링크 유효성체크//

		var youUrl = /(http:|https:)?(\/\/)?(www\.)?(youtube.com|youtu.be)\/(watch|embed)?(\?v=|\/)?(\S+)?/g;
		function isYoutube() {
			var youtube = document.querySelector('[name="recipeVideo"]');
			if (!youUrl.test(youtube.value)) {
				alert("유튜브 주소는 embed이 포함된 고유주소로 입력해주세요")
				youtube.value = "";

			}

		}

		///설명 추가/삭제////
		var detailForm = document.querySelector('#REdetail');
		var addplace = document.querySelector('#addplace');
		var count = 3;
		function addDe() {
			if (count <= 10) {
				addplace.appendChild(detailForm.cloneNode(true));
				document.querySelectorAll('.REdetailC')[document
						.querySelectorAll('.REdetailC').length - 1].childNodes[1].childNodes[1].childNodes[1].value = "";
				document.querySelectorAll('.REdetailC')[document
						.querySelectorAll('.REdetailC').length - 1].childNodes[5].childNodes[1].childNodes[1].childNodes[3].childNodes[3].value = "";

				count++;
			}

		}

		function removeDe() {
			var detailFormClass = document.querySelectorAll('.REdetailC');
			if (count > 3) {
				detailFormClass[detailFormClass.length - 1].remove();
				count--;
			}

		}

		function loadFile(event) {
			var output = document.getElementById('output');
			output.src = URL.createObjectURL(event.target.files[0]);
			output.onload = function() {
				URL.revokeObjectURL(output.src) // free memory
			};
			imgCheck()
		};

		///// 이미지파일 체크 시작 ////
		var imgFile = document.querySelectorAll('.isFile');
		var fileForm = /(.*?)\.(jpg|jpeg|png|gif|bmp)$/i;
		var fileSize;
		function checkMainPic() {
			if (imgFile[0].value == "") {
				alert("대표사진은 필수입니다!");

			}
		};

		function imgCheck() {
			for (var i = 0; i < imgFile.length; i++) {
				if (imgFile[i].value != "") {

					if (!imgFile[i].value.match(fileForm)) {
						alert("이미지 파일만 업로드 가능");
						imgFile[i].value = "";

					}
				}
			}

		};

		/////이미지 확장자 체크 종료///

		function imgView(obj) {

			var imgid = obj.previousElementSibling;

			imgid.src = URL.createObjectURL(event.target.files[0]);
			imgid.onload = function() {
				URL.revokeObjectURL(imgid.src) // free memory

			}
			imgCheck()

		};

		function mainPicDel() {
			imgFile[0].value = "";
			output.removeAttribute('src');
		}

		function PicDel(obj) {
			var imgfiles = obj.nextElementSibling.childNodes[1];
			var imgfilesinput = obj.nextElementSibling.childNodes[3];
			imgfilesinput.value = "";
			imgfiles.removeAttribute('src');

		}
	</script>

</body>
</html>
  • 작성 창을 복사하여 controller에서 값을 가지고와 value를 ${}로 지정해준게 전부이다
    • 하지만 이 경우 재료 폼이 forEach로 반복되는 경우 만들어진 틀 자체가 무너질수 있었다.

    • 나는 재료폼을 입력하지 않은 만큼은 공백으로 하고 입력한 것만 출력되길 바랐다.

    • 검색해보니 jsp를 사용할때 전송받은 List는 ${ListName.get(index).name}으로 출력할수 있었다. 이렇게 해서

      <input type="text" class="form-control" id="" maxlength="10"
      <c:if test="${rmList.get(0) ne null}> value="${rmList.get(0).material}</c:if> >
    • 로 사용하면 될줄알았으나.. 지속적은 오류메세지가 떴다.

      jsp에서 오류가 발생했기에 back의 문제는 아니었다.

    • 문제는 index가 충분하지 않을 때 그러니까 index가 0밖에 없는 index 1이 있는지 확인을 할때 생겼다. index 1이 아예 존재하지 않으니 null체크도 할수가 없었던것이다.

    • 방법을 바꿔 List의 size를 파악하기로 했으나 List size는 c태그로는 파악할수 없었다. 그래서 controller에서 받아오기로 했다.

Controller

/**
 * 레시피 수정창 연결
 * @param mv
 * @param recipeNo
 * @param session
 * @return
 */
	@RequestMapping(value="/recipe/modifyForm.do", method = RequestMethod.GET)
	public ModelAndView recipeModifyView(ModelAndView mv, @RequestParam("recipeNo") Integer recipeNo, HttpSession session) {
		
		try {
		Recipe recipe = rService.printOneRecipe(recipeNo);
		RecipeTag rTag = rService.printOneRecipeTag(recipeNo);
		List<RecipeMaterial> rmList = rService.printOneRecipeMaterial(recipeNo);
		List<RecipeStep> rsList = rService.printOneRecipeStep(recipeNo);
		
		mv.addObject("recipe", recipe);
		mv.addObject("rTag", rTag);
		mv.addObject("rmList", rmList);
		mv.addObject("rmListSize",rmList.size());
		mv.addObject("rsList", rsList);
		mv.setViewName("/recipe/recipeModifyForm");
		}catch (Exception e) {
			mv.addObject("msg", e.getMessage());
			mv.setViewName("common/error");
		}
		return mv;
	}
  • 서비스로 연결되는 메소드들은 이미 상세창에서 연결할떄 쓴 메소드라 새로 만든건 없었다. 상세창 메소드에 관해서는 [세미프로젝트] 9월 18일 구현 (velog.io) 에 나와있다.
  • mv.addObject("rmListSize",rmList.size()); 를 controller에서 구하여 jsp로 전송했고 jsp에서는
<input type="text" class="form-control" id="" maxlength="10"
	<c:if test="${rmListSize >=1  }"> value="${rmList.get(0).material }"</c:if>	name="material"> <label for="floatingInput">재료명</label>
  • 이렇게 입력받은 사이즈를 활용하여 c:if문을 만들수있었다.

내일은 수정창에서 상세 입력폼을 10개 이상 만들수있는 버그를 수정하고 사진 삭제/수정이 가능하게 한 뒤 댓글을 진행할 예정이다.

profile
hello world

0개의 댓글