게시글 작성을 해보자

게시글 작성 구현 전 알아야 할 것!

  • 목록 조회 : /board/1?cp=1 (cp : current page(현재페이지))
  • 상세 조회 : /board/1/1500?cp=1

  • 컨트롤러 따로 생성
  • 삽입 : /board2/1/inssert?code=1(code ==BOARD_CODE , 게시판 종류)
  • 수정 : /board2/1/update?code=1&no=1500 (no == BOARD_NO , 게시글 번호)
  • 삭제 : /board2/1/delete?code=1&no=1500

board.controller

BoardController2

package edu.kh.project.board.controller;

import java.io.IOException;
import java.util.List;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.fasterxml.jackson.annotation.JacksonInject.Value;

import edu.kh.project.board.model.dto.Board;
import edu.kh.project.board.model.service.BoardService2;
import edu.kh.project.member.model.dto.Member;

@Controller
@RequestMapping("/board2")
@SessionAttributes({"loginMember"})
public class BoardController2 {
	
	@Autowired
	private BoardService2 service;
	
	// 게시글 작성 화면 전환
	@GetMapping("/{boardCode:[0-9]+}/insert")
	public String boardInsert(@PathVariable("boardCode") int boardCode) {
		
		//@PathVariable :주소 값 가져오기 + requset scope 에 값올리기
		return "board/boardWrite";
	}

	
	// 게시글 작성 
	@PostMapping("/{boardCode:[0-9]+}/insert")
	public String boardInsert( 
			@PathVariable("boardCode") int boardCode
			, Board board /*커멘드 객체(필드에 파라미터 담겨져 있음)*/
			, @RequestParam(value="images", required = false) List<MultipartFile> images
			, @SessionAttribute("loginMember") Member loginMember
			, RedirectAttributes ra
			, HttpSession session) throws IllegalStateException, IOException{
		
		
		// 파라미터 : 제목, 내용, 파일(0~5개)
		// 파일저장 경로 : httpSession
		// 세션 : 로그인한 회원 번호 
		//리다이렉트 시 데이터 전달 : RedirectAuttributrs
		// 작성 성공 시 이동할 게시판 코드 : @PathVariable("boardCode")
		
		/* List<MultipartFile>
		 * -업로드된 이미지가 없어도 List에 요소 MultipartFile  객체가 추가됨
		 * 
		 * -단, 업로드된 이미지가 없는 MultipartFile 객체는
		 * 파일크기(size)가 0 또는 파일명(getOriginalFileName())이 ""
		 * */
		
		// 1. 로그인한 회원 번호를 얻어와 board에 세팅
		board.setMemberNo(loginMember.getMemberNo());
		
		// 2. boardCode도 board에 세팅
		board.setBoardCode(boardCode);
		
		// 3. 업로드된 이밎 서버에 실제로 저장되는 경로
		//    + 웹에서 요청 시 이미지를 볼 수 있는 경로
		String webPath ="/resources/images/board/";
		String filePath = session.getServletContext().getRealPath(webPath);

		// 게시글 삽입을 하는 서비스 호출 후 삽입된 게시글의 번호 반환 받기
		int boardNo = service.boardInsert(board, images, webPath, filePath);
		
		// 게시글 삽입 셩공 시 
		// -> 방금 삽입한 게시글의 상세 조회 페이지로 리다이렉트
		// -> /board/{boardCode}/{boardNo}

		String message = null;
		String path ="redirect:";
		
		if(boardNo > 0) { //성공 시
			message="게시글이 등록 되었습니다.";
			path +="/board/"+boardCode +"/"+boardNo;
		}else {
			message="게시글 등록 실패ㅠㅠ";
			path += "insert";
		}
		
		ra.addFlashAttribute("message", message);
		return path;
	}
}

board.model

service.BoardService2 인터페이스

/** 게시글 삽입
	 * @param board
	 * @param images
	 * @param webPath
	 * @param filePath
	 * @return boardNo
	 */
	int boardInsert(Board board, List<MultipartFile> images, String webPath, String filePath)throws IllegalStateException, IOException;

service.BaordServiceImpl2 클래스

package edu.kh.project.board.model.service;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import edu.kh.project.board.common.utility.Util;
import edu.kh.project.board.model.dao.BoardDAO2;
import edu.kh.project.board.model.dto.Board;
import edu.kh.project.board.model.dto.BoardImage;
import edu.kh.project.board.model.exception.FileUploadException;

@Service
public class BoardServiceImpl2 implements BoardService2{

	@Autowired
	private BoardDAO2 dao;
	
	
	// 게시글 삽입 
	@Override
	@Transactional(rollbackFor = Exception.class)
	public int boardInsert(Board board, List<MultipartFile> images, String webPath, String filePath) throws IllegalStateException, IOException {

		// 0. XSS 방지 처리
		board.setBoardContent(Util.XSSHandling(board.getBoardContent()));
		board.setBoardTitle(Util.XSSHandling(board.getBoardTitle()));
		
		
		// 1. BOARD 테이블에 INSERT하기(제목, 내용, 작성자, 게시판 코드)
		// -> boardNo (시퀀스로 생성한 번호) 반환 받기
		int boardNo = dao.boardInsert(board);
		
		// 2. 게시글 삽입 성공시 
		// 업로드 된 이미지가 있다면 BOARD_IMG 테이블에 삽입하는 DAO 호출
		
		if(boardNo > 0) { // 게시글 성공 시

			// List<MultipartFile> images
			// -> 업로드된 파일이 담긴 객체 MultipartFile이 5개 존재
			// -> 단, 업로드된 파일이 없어도 MultipartFile 개체는 존재
			
			// 실제로 업로드된 파일을 기록할 List
			List<BoardImage> uploadList = new ArrayList<BoardImage>();
			
			// images에 담겨 잇는 파일 중에서 실제로 업로드된 파일 만 분류
			for(int i= 0; i <images.size(); i++) {
			
				//i 번째 요소에 업로드한 파일이 있다면
				if(images.get(i).getSize() > 0) {
					
					BoardImage img = new BoardImage();
					
					//img에 파일 정보를 담아서 uploadList에 추가 
					img.setImagePath(webPath); //웹 접근 경로
					img.setBoardNo(boardNo); //게시글 번호 
					img.setImageOrder(i); //이미지 순서
				
					//파일 원본명
					String fileName = images.get(i).getOriginalFilename();
					
					img.setImageOriginal(fileName); //원본명
					img.setImageReName(Util.fileRename(fileName)); // 변경명
					
					uploadList.add(img);
				}
			} // 분류하는 for문 종료
			
			// 분류 작업 후 uploadList가 비어 있지 않은 경우
			// == 업로드한 파일이 있다
			if(!uploadList.isEmpty()) {
				
				// BOARD_IMG테이블에 INSERT 하는 DAO호출
				int result = dao.insertImageList(uploadList);
				// result == 삽입된 행의 개수 == uploadList.size()
				
				// 삽입된 행의 개수와 uploadList의 개수가 같다면
				// == 전체 insert 성공
				if(result == uploadList.size()) {
					
					// 서버에 파일 저장(transferTo())
					
					// images			: 실제 파일이 담긴 객체 리스트
					//						(업로드 안된 인덱스 빈칸)
					
					// uploadList 		: 업로 된 파일의 정보 리스트
					//			(원본명, 변경명, 순서, 경로, 게시글 번호)
					
					// 순서 == images 업로드된 인덱스 번호
					
					for(int i =0; i <uploadList.size(); i++) {
						
						int index = uploadList.get(i).getImageOrder();
						
						
						// 파일로 변환 
						String rename = uploadList.get(i).getImageReName();
						
						images.get(index).transferTo(new File(filePath+ rename));
					}
					
				}else { //일부 또는 전체 insert 실패
					
					// **웹서비스 실행 중 1개라도 실패하면 전체 실패**
					// -> rollback필요

					// @Transactional(rollbackFor = Exception.class)
					// -> 예외가 발생 해야지만 롤백
					
					// [결론]
					// 예외 강제 발생 시켜서 rollback  해야한다
					// -> 사용자 정의 예외 생성
					
					throw new FileUploadException(); //예외 강제 발생
				}
			}
		}
		
		return boardNo;
	}
}

dao.BoardDAO2

package edu.kh.project.board.model.dao;

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import edu.kh.project.board.model.dto.Board;
import edu.kh.project.board.model.dto.BoardImage;

@Repository
public class BoardDAO2 {

	@Autowired
	private SqlSessionTemplate sqlsession;
	
	/** 게시글 삽입
	 * @param board
	 * @return 
	 */
	public int boardInsert(Board board) {
		int result = sqlsession.insert("boardMapper.boardInsert", board);
		
		// ->sql 수행 후 매개변수 board 객체에는 boardNo 존재O
		
		// 삽입 성공시 
		if(result > 0) result =  board.getBoardNo();
		
		return result; // 삽입 성공 시 boardNo, 실패 시 0 반환
	}


	/** 이미지 리스트(여러개)삽입
	 * @param uploadList
	 * @return result
	 */
	public int insertImageList(List<BoardImage> uploadList) {
		return sqlsession.insert("boardMapper.insertImageList", uploadList);
	}
}

board-mapper.xml

게시글 삽입(INSERT) 시 미리 boardNo 결과를 Select 구문으로 얻어와서 INSERT구문에 넣어 삽입 결과 1 or 0 얻어오기!


useGeneratedKeys 속성 : DB 내부적으로 생성한 key(시퀀스)를 전달된 파라미터의 필드로 대입 가능 여부를 지정(trur/false)
동적 SQL
프로그램 수행 중 SQL를 변경하는 기능 (마이바티스의 가장 강력한 기능)
selectKey태그
INSERT/UPDATE 시 사용할 키(시퀀스)를 조회해서 파라미터의 지정된 필드에 대입
oder속성
메인 SQL이 수행되기 전/후 selectKey가 수행되도록 지정한다
전: BEFORE / 후: AFTER
foreach
이미지 리스트 (여러개 삽입)

	<!-- 게시글 삽입 -->
	<!-- 
		useGeneratedKeys 속성 : DB 내부적으로 생성한 key(시퀀스)를
							  전달된 파라미터의 필드로 대입 가능 여부를 지정
	**동적 SQL**
	- 프로그램 수행 중 SQL를 변경하는 기능 (마이바티스의 가장 강력한 기능)
	
	<selectKey> 태그 : INSERT/UPDATE 시 사용할 키(시퀀스)를 
					  조회해서 파라미터의 지정된 필드에 대입	
					  
	oder속성 : 메인 SQL이 수행되기 전/후 selectKey가 수행되도록 지정한다
			전: BEFORE
			후: AFTER
			
			
	keyProperty 속성 : selectKey 조회 결과를 저장할 파라미터의 필드
 -->
<insert id="boardInsert" parameterType="Board" useGeneratedKeys="true">
	
	<selectKey order="BEFORE" resultType="_int" keyProperty="boardNo">
		SELECT SEQ_BOARD_NO.NEXTVAL FROM DUAL
	</selectKey>
	
	INSERT INTO BOARD 
      VALUES( #{boardNo},
              #{boardTitle},
              #{boardContent},
              DEFAULT, DEFAULT, DEFAULT, DEFAULT,
              #{memberNo}, 
              #{boardCode} )
</insert>

<!-- 동적 SQL 중 <foreach> 
  - 특정 SLQ 구문을 반복할 때 사용
  - 반복되는 사이에 구분자(separator)를 추가할 수 있음.
  
  collection : 반복할 객체의 타입 작성(list, set, map...)
  item : collection에서 순차적으로 꺼낸 하나의 요소를 저장하는 변수
  index : 현재 반복 접근중인 인덱스 (0,1,2,3,4 ..)
  
  open : 반복 전에 출력할 sql
  close : 반복 종료 후에 출력한 sql
  
  separator : 반복 사이사이 구분자
-->



<!-- 이미지 리스트(여러개 삽입) -->
<insert id="insertImageList" parameterType="list">
	INSERT INTO BOARD_IMG
	SELECT SEQ_IMG_NO.NEXTVAL, A.*
	FROM(
	
		<foreach collection="list" item="img" separator=" UNION ALL ">
			SELECT  #{img.imagePath} IMG_PATH,
				 	#{img.imageReName} IMG_RENAME,
				 	#{img.imageOriginal} IMG_ORIGINAL,
		            #{img.imageOrder} IMG_ORDER,
		            #{img.boardNo} BOARD_NO
	        FROM DUAL;
		</foreach>

        ) A
</insert>
  
##  VS CODE
### boardList.jsp
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>

<%-- map에 저장된 값을 각각 변수에 저장 --%>
<c:set var="pagination" value="${map.pagination}"/>
<c:set var="boardList" value="${map.boardList}"/>

<%-- <c:set var="boardName" value="${boardTypeList[boardCode-1].BOARD_NAME}"/> --%>
<c:forEach items="${boardTypeList}" var="boardType">
    <c:if test="${boardType.BOARD_CODE == boardCode}" >
        <c:set var="boardName" value="${boardType.BOARD_NAME}"/>
    </c:if>
</c:forEach>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>

    <link rel="stylesheet" href="/resources/css/board/boardList-style.css">

</head>
<body>
    <main>
        <jsp:include page="/WEB-INF/views/common/header.jsp"/>

        
        <section class="board-list">

            <h1 class="board-name">${boardName}</h1>


            <div class="list-wrapper">
                <table class="list-table">
                    
                    <thead>
                        <tr>
                            <th>글번호</th>
                            <th>제목</th>
                            <th>작성자</th>
                            <th>작성일</th>
                            <th>조회수</th>
                            <th>좋아요</th>
                        </tr>
                    </thead>

                    <tbody>
                        <c:choose>
                           <c:when test="${empty boardList}">
                                <%-- 조회된 게시글 목록 비어있거나 null인 경우 --%>
                                
                                <!-- 게시글 목록 조회 결과가 비어있다면 -->
                                <tr>
                                    <th colspan="6">게시글이 존재하지 않습니다.</th>
                                </tr>
                           </c:when>
                        
                           <c:otherwise>

                            <c:forEach items="${boardList}" var="board">
                                <!-- 게시글 목록 조회 결과가 있다면-->
                                <tr>
                                    <td>${board.boardNo}</td>
                                    <td> 
                                        <%-- 썸네일이 있을 경우 --%>
                                        <c:if test="${!empty board.thumbnail}" >
                                            <img class="list-thumbnail" src="${board.thumbnail}">
                                        </c:if>



                                        <%-- ${boardCode} : @PathVariable로 request scope에 추가된 값 --%>
                                        <a href="/board/${boardCode}/${board.boardNo}?cp=${pagination.currentPage}">${board.boardTitle}</a>   
                                        [${board.commentCount}]                       
                                    </td>
                                    <td>${board.memberNickname}</td>
                                    <td>${board.boardCreateDate}</td>
                                    <td>${board.readCount}</td>
                                    <td>${board.likeCount}</td>
                                </tr>
                            </c:forEach>
                           </c:otherwise>
                        </c:choose>

                         

                        
                    </tbody>
                </table>
            </div>


            <div class="btn-area">

            <!-- 로그인 상태일 경우 글쓰기 버튼 노출 -->
                <c:if test="${!empty loginMember}" >
                    <button id="insertBtn">글쓰기</button>                     
                </c:if>

            </div>


            <div class="pagination-area">


                <ul class="pagination">
                
                    <!-- 첫 페이지로 이동 -->
                    <li><a href="/board/${boardCode}?cp=1">&lt;&lt;</a></li>

                    <!-- 이전 목록 마지막 번호로 이동 -->
                    <li><a href="/board/${boardCode}?cp=${pagination.prevPage}">&lt;</a></li>

               
                    <!-- 특정 페이지로 이동 -->
                    <c:forEach var="i" begin="${pagination.startPage}"
                                end="${pagination.endPage}" step="1">
                        <c:choose>
                           <c:when test="${i== pagination.currentPage}">
                                <!-- 현재 보고있는 페이지 -->
                                <li><a class="current">${i}</a></li>
                           </c:when>
                        
                           <c:otherwise>
                                <!-- 현재 페이지를 제외한 나머지 -->
                                <li><a href="/board/${boardCode}?cp=${i}">${i}</a></li>
                           </c:otherwise>
                        </c:choose>
                    </c:forEach>
                    
                    <!-- 다음 목록 시작 번호로 이동 -->
                    <li><a href="/board/${boardCode}?cp=${pagination.nextPage}">&gt;</a></li>

                    <!-- 끝 페이지로 이동 -->
                    <li><a href="/board/${boardCode}?cp=${pagination.maxPage}">&gt;&gt;</a></li>

                </ul>
            </div>


         <!-- 검색창 -->
            <form action="#" method="get" id="boardSearch">

                <select name="key" id="searchKey">
                    <option value="t">제목</option>
                    <option value="c">내용</option>
                    <option value="tc">제목+내용</tion>
                    <option value="w">작성자</option>
                </select>

                <input type="text" name="query"  id="searchQuery" placeholder="검색어를 입력해주세요.">

                <button>검색</button>
            </form>

        </section>
    </main>
    
    
    <!-- 썸네일 클릭 시 모달창 출력 -->
    <div class="modal">
        <span id="modalClose">&times;</span>
        <img id="modalImage" src="/resources/images/user.png">
    </div>


    <jsp:include page="/WEB-INF/views/common/footer.jsp"/>
    <script src="/resources/js/board/boardList.js"></script>

</body>
</html>

boardList.js

const insertBtn = document.getElementById("insertBtn");

// 글쓰기 버튼 을 클릭했을 때 

if(insertBtn != null){

    insertBtn.addEventListener("click", ()=>{
        // JS BOM 객체 location

        // location.href="주소"
        // 해당 주소 요청(GET방식)
       
        // location.href="/board2/"+location.pathname.split("/")[2]
        location.href=`/board2/${location.pathname.split("/")[2]}/insert`
                    //board2/1/insert
    })
}

boardWrite.jsp

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

<c:forEach items="${boardTypeList}" var="boardType">
    <c:if test="${boardType.BOARD_CODE == boardCode}" >
        <c:set var="boardName" value="${boardType.BOARD_NAME}"/>
    </c:if>
</c:forEach>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${boardName}</title>

    <link rel="stylesheet" href="/resources/css/board/boardWrite-style.css">
</head>
<body>
    <main>
        <jsp:include page="/WEB-INF/views/common/header.jsp"/>


                            <%-- @PathVariable에서 request scope로 가져옴 --%>
        <form action="/board2/${boardCode}/insert" method="POST" 
                class="board-write" id="boardWriteFrm" enctype="multipart/form-data"> 
                         <%--enctype="multipart/form-data" : 제출 이코딩 X
                         -> 파일제출 가능
                         -> Multipath Resolver 가 문자열, 파일 구분
                         --> 문자열, String, int, DTO, Map (HttpMessageConverter)
                         --> 파일 -> MultiPathFile 객체 -> transferTo() (파일을 서버에 저장) --%>
           
            <h1 class="board-name">${boardName}</h1>

            <!-- 제목 -->
            <h1 class="board-title">
                <input type="text" name="boardTitle" placeholder="제목" value="">
            </h1>


            <!-- 썸네일 영역 -->
            <h5>썸네일</h5>
            <div class="img-box">
                <div class="boardImg thumbnail">
                    <label for="img0">
                        <img class="preview" src="">
                    </label>
                    <input type="file" name="images" class="inputImage" id="img0" accept="image/*">
                    <span class="delete-image">&times;</span>
                </div>
            </div>


            <!-- 업로드 이미지 영역 -->
            <h5>업로드 이미지</h5>
            <div class="img-box">

                <div class="boardImg">
                    <label for="img1">
                        <img class="preview" src="">
                    </label>
                    <input type="file" name="images" class="inputImage" id="img1" accept="image/*">
                    <span class="delete-image">&times;</span>
                </div>

                <div class="boardImg">
                    <label for="img2">
                        <img class="preview" src="">
                    </label>
                    <input type="file" name="images" class="inputImage" id="img2" accept="image/*">
                    <span class="delete-image">&times;</span>
                </div>

                <div class="boardImg">
                    <label for="img3">
                        <img class="preview" src="">
                    </label>
                    <input type="file" name="images" class="inputImage" id="img3" accept="image/*">
                    <span class="delete-image">&times;</span>
                </div>

                <div class="boardImg">
                    <label for="img4">
                        <img class="preview" src="">
                    </label>
                    <input type="file" name="images" class="inputImage" id="img4" accept="image/*">
                    <span class="delete-image">&times;</span>
                </div>
            </div>

            <!-- 내용 -->
            <div class="board-content">
                <textarea name="boardContent"></textarea>
            </div>


             <!-- 버튼 영역 -->
            <div class="board-btn-area">
                <button type="submit" id="writebtn">등록</button>
            </div>

            
        </form>

    </main>

    <jsp:include page="/WEB-INF/views/common/footer.jsp"/>

    <script src="/resources/js/board/boardWrite.js"></script>

</body>
</html>

boardWrite.js

//미리보기 관련 요소 모두 얻어오기

//img 5개
const preview = document.getElementsByClassName("preview");
//file 5개
const inputImage = document.getElementsByClassName("inputImage");

// X버튼 5개
const deleteImage = document.getElementsByClassName("delete-image");

// 위에 얻어온 요소들의 개수가 같음 == 인덱스가 일치함

for( let i=0; i < inputImage.length; i++){
    
    // 파일이 선택되거나, 선택 후 취소 되었을 때
    inputImage[i].addEventListener("change", e=>{
        
        const file = e.target.files[0]; //선택된 파일의 데이터

        if(file != undefined){ //파일이 선택되었을 때

            const reader = new FileReader(); //파일을 읽는 객체

            reader.readAsDataURL(file);
            //지정된 파일을 읽은 후 result변수에 URL형식으로 저장

            reader.onload = e =>{ //파일을 다 읽은 후 수행
                preview[i].setAttribute("src", e.target.result);
            }

        }else{ // 파일 선택 후 취소 눌렀을 때
            // -> 선택된 파일이 없다 -> 미리보기 없다
            preview[i].removeAttribute("src");


            
        }
    });

    // 미리보기 삭제 버튼(x버튼)
    deleteImage[i].addEventListener("click", ()=>{
       
        // 미리보기 이미지가 있을 경우 
        if(preview[i].getAttribute("src") != ""){
            
            // 미리보기 삭제
            preview[i].removeAttribute("src");
            
            // input type="file" 태그의 value를 삭제
            // input type="file" 의 value ""(빈칸)만 대입가능
            inputImage[i].value="";
        }
    })


}

// 게시글 등록 시 제목, 내용 작성 여부 검사 
const boardWriteFrm = document.getElementById("boardWriteFrm")
const boardTilte = document.querySelector("[name='boardTitle']")
const boardContent = document.querySelector("[name='boardContent']")

boardWriteFrm.addEventListener("submit", e=>{

    if( boardTilte.value.trim().length == 0 ){
        alert("제목을 입력해주세요")
        boardTilte.value="";
        boardTilte.focus();
        e.preventDefault();
        return;
    }
    
    if( boardContent.value.trim().length == 0){
        alert("내용을 입력해주세요");
        boardContent.value="";
        boardContent.focus();
        e.preventDefault();
        return;
    }
});
profile
나를 죽이지 못하는 오류는 내 코드를 더 강하게 만들지ㅋ

0개의 댓글